bitkeeper revision 1.645 (3fd43302Q7iIIXRlzLUoi_opKLWYIg)
authorkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Mon, 8 Dec 2003 08:14:58 +0000 (08:14 +0000)
committerkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Mon, 8 Dec 2003 08:14:58 +0000 (08:14 +0000)
Many files:
  new file
Makefile:
  New SCSI driver for sym53c8xx

20 files changed:
.rootkeys
xen/drivers/scsi/Makefile
xen/drivers/scsi/sym53c8xx_2/ChangeLog.txt [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/Documentation.txt [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/Makefile [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/sym53c8xx.h [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/sym_conf.h [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/sym_defs.h [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/sym_fw.c [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/sym_fw.h [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/sym_fw1.h [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/sym_fw2.h [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/sym_glue.c [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/sym_glue.h [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/sym_hipd.c [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/sym_hipd.h [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/sym_malloc.c [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/sym_misc.c [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/sym_misc.h [new file with mode: 0644]
xen/drivers/scsi/sym53c8xx_2/sym_nvram.c [new file with mode: 0644]

index 23ce80d11385207eb4a6b1d3b056a84d4b71cf97..1d7ee6c8e1a77cb98ff101ba8d3db605eb09cdde 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 3ddb79beC6PIqDEaxAfO3bLKcmMLeA xen/drivers/scsi/scsicam.c
 3ddb79bedAG8DPsr3S1N4IASxUuBug xen/drivers/scsi/sd.c
 3ddb79beA27dAK0xtNh4k6SJniKnlA xen/drivers/scsi/sd.h
+3fd432fb0fW430_7mzTegOyjjFB-Iw xen/drivers/scsi/sym53c8xx_2/ChangeLog.txt
+3fd432fbzXmGBq60nilWbfafyDXrtQ xen/drivers/scsi/sym53c8xx_2/Documentation.txt
+3fd432fbVpHp1vv8A6GQBZQnKMFS7Q xen/drivers/scsi/sym53c8xx_2/Makefile
+3fd432fbAOAdNUuPUOlRmT723POpnQ xen/drivers/scsi/sym53c8xx_2/sym53c8xx.h
+3fd432fbO-kotluBDNXwt3AJ_yiuAw xen/drivers/scsi/sym53c8xx_2/sym_conf.h
+3fd432fbFiHM-XyIhDIU3wLuVTEe8Q xen/drivers/scsi/sym53c8xx_2/sym_defs.h
+3fd432fbc6E0FNW2CfDhY18WV4fIcA xen/drivers/scsi/sym53c8xx_2/sym_fw.c
+3fd432fbEYdbqLET81nisgPiVVlRtA xen/drivers/scsi/sym53c8xx_2/sym_fw.h
+3fd432fbiymG2mfG5k2HHz1w1hPcHQ xen/drivers/scsi/sym53c8xx_2/sym_fw1.h
+3fd432fbITAwK8BK6-6c8yPfxpxROg xen/drivers/scsi/sym53c8xx_2/sym_fw2.h
+3fd432fbr5_OuwNUNPjLfbDdS9iMLw xen/drivers/scsi/sym53c8xx_2/sym_glue.c
+3fd432fbRLKL6mW2K5n_pHiTnMxA3Q xen/drivers/scsi/sym53c8xx_2/sym_glue.h
+3fd432fbkCURaCXa7kKXvnHI1Y0B6A xen/drivers/scsi/sym53c8xx_2/sym_hipd.c
+3fd432fbpPgJcz6YrCxWNx934bK7BQ xen/drivers/scsi/sym53c8xx_2/sym_hipd.h
+3fd432fbHyYMzfNAB_5mKGOvh1HTrg xen/drivers/scsi/sym53c8xx_2/sym_malloc.c
+3fd432fbFBLr85q2xIgdvDR7S2kvlw xen/drivers/scsi/sym53c8xx_2/sym_misc.c
+3fd432fbOri8Ue9-QTi6X-WviC2JYA xen/drivers/scsi/sym53c8xx_2/sym_misc.h
+3fd432fbM9qma6nz_-GWRpZ7v1pqrg xen/drivers/scsi/sym53c8xx_2/sym_nvram.c
 3ddb79c3l4IiQtf6MS2jIzcd-hJS8g xen/include/asm-i386/apic.h
 3ddb79c3QJYWr8LLGdonLbWmNb9pQQ xen/include/asm-i386/apicdef.h
 3ddb79c3OiG9eTsi9Dy3F_OkuRAzKA xen/include/asm-i386/atomic.h
index a23accb5e3808db722dce774aef73c4ce6475ef0..cdce09cd40a701c949711cf183cdcc660b875bb8 100644 (file)
@@ -6,14 +6,16 @@ SCSI_OBJS := scsi.o hosts.o scsicam.o scsi_dma.o scsi_error.o
 SCSI_OBJS += scsi_ioctl.o scsi_lib.o scsi_merge.o scsi_proc.o 
 SCSI_OBJS += scsi_queue.o scsi_scan.o scsi_syms.o scsi_obsolete.o
 SCSI_OBJS += constants.o sd.o aacraid/aacraid.o aic7xxx/aic7xxx.o
-SCSI_OBJS += megaraid.o BusLogic.o
+SCSI_OBJS += megaraid.o BusLogic.o sym53c8xx_2/sym53c8xx.o
 
 default: $(OBJS)
        $(MAKE) -C aacraid
        $(MAKE) -C aic7xxx
+       $(MAKE) -C sym53c8xx_2
        $(LD) -r -o driver.o $(SCSI_OBJS)
 
 clean:
        $(MAKE) -C aacraid clean
        $(MAKE) -C aic7xxx clean
+       $(MAKE) -C sym53c8xx_2 clean
        rm -f *.o *~ core
diff --git a/xen/drivers/scsi/sym53c8xx_2/ChangeLog.txt b/xen/drivers/scsi/sym53c8xx_2/ChangeLog.txt
new file mode 100644 (file)
index 0000000..6a4a3f8
--- /dev/null
@@ -0,0 +1,148 @@
+Sat Dec 30 21:30 2000 Gerard Roudier 
+       * version sym-2.1.0-20001230
+       - Initial release of SYM-2.
+
+Mon Jan 08 21:30 2001 Gerard Roudier 
+       * version sym-2.1.1-20010108
+       - Change a couple of defines containing ncr or NCR by their 
+         equivalent containing sym or SYM instead.
+
+Sun Jan 14 22:30 2001 Gerard Roudier 
+       * version sym-2.1.2-20010114
+       - Fix a couple of printfs:
+         * Add the target number to the display of transfer parameters.
+         * Make the display of TCQ and queue depth clearer.
+
+Wed Jan 17 23:30 2001 Gerard Roudier 
+       * version sym-2.1.3-20010117
+       - Wrong residual values were returned in some situations.
+         This broke cdrecord with linux-2.4.0, for example.
+
+Sat Jan 20 18:00 2001 Gerard Roudier 
+       * version sym-2.1.4-20010120
+       - Add year 2001 to Copyright.
+       - A tiny bug in the dma memory freeing path has been fixed.
+         (Driver unload failed with a bad address reference).
+
+Wed Jan 24 21:00 2001 Gerard Roudier 
+       * version sym-2.1.5-20010124
+       - Make the driver work under Linux-2.4.x when statically linked 
+         with the kernel.
+       - Check against memory allocation failure for SCRIPTZ and add the 
+         missing free of this memory on instance detach.
+       - Check against GPIO3 pulled low for HVD controllers (driver did 
+         just the opposite).
+         Misdetection of BUS mode was triggerred on module reload only, 
+         since BIOS settings were trusted instead on first load.
+
+Wed Feb 7 21:00 2001 Gerard Roudier 
+       * version sym-2.1.6-20010207
+       - Call pci_enable_device() as wished by kernel maintainers.
+       - Change the sym_queue_scsiio() interface.
+         This is intended to simplify portability.
+       - Move the code intended to deal with the dowloading of SCRIPTS 
+         from SCRIPTS :) in the patch method (was wrongly placed in 
+         the SCRIPTS setup method).
+       - Add a missing cpu_to_scr()  (np->abort_tbl.addr)
+       - Remove a wrong cpu_to_scr() (np->targtbl_ba)
+       - Cleanup a bit the PPR failure recovery code.
+
+Sat Mar 3 21:00 2001 Gerard Roudier 
+       - Add option SYM_OPT_ANNOUNCE_TRANSFER_RATE and move the 
+         corresponding code to file sym_misc.c.
+         Also move the code that sniffes INQUIRY to sym_misc.c.
+         This allows to share the corresponding code with NetBSD 
+         without polluating the core driver source (sym_hipd.c).
+       - Add optionnal code that handles IO timeouts from the driver.
+         (not used under Linux, but required for NetBSD)
+       - Donnot assume any longer that PAGE_SHIFT and PAGE_SIZE are 
+         defined at compile time, as at least NetBSD uses variables 
+         in memory for that.
+       - Refine a work-around for the C1010-33 that consists in 
+         disabling internal LOAD/STORE. Was applied up to revision 1.
+         Is now only applied to revision 0.
+       - Some code reorganisations due to code moves between files.
+
+Tues Apr 10 21:00 2001 Gerard Roudier 
+       * version sym-2.1.9-20010412
+       - Reset 53C896 and 53C1010 chip according to the manual.
+         (i.e.: set the ABRT bit in ISTAT if SCRIPTS are running)
+       - Set #LUN in request sense only if scsi version <= 2 and
+         #LUN <= 7.
+       - Set busy_itl in LCB to 1 if the LCB is allocated and a 
+         SCSI command is active. This is a simplification.
+       - In sym_hcb_free(), donnot scan the free_ccbq if no CCBs 
+         has been allocated. This fixes a panic if attach failed.
+       - Add DT/ST (double/simple transition) in the transfer 
+         negotiation announce.
+       - Forces the max number of tasks per LUN to at least 64.
+       - Use pci_set_dma_mask() for linux-2.4.3 and above.
+       - A couple of comments fixes.
+
+Wed May 22:00 2001 Gerard Roudier 
+       * version sym-2.1.10-20010509
+       - Mask GPCNTL against 0x1c (was 0xfc) for the reading of the NVRAM.
+         This ensure LEDC bit will not be set on 896 and later chips.
+         Fix sent by Chip Salzenberg <chip@perlsupport.com>.
+       - Define the number of PQS BUSes supported.
+         Fix sent by Stig Telfer <stig@api-networks.com>
+       - Miscellaneous common code rearrangements due to NetBSD accel 
+         ioctl support, without impact on Linux (hopefully).
+
+Mon July 2 12:00 2001 Gerard Roudier 
+       * version sym-2.1.11-20010702
+       - Add Tekram 390 U2B/U2W SCSI LED handling.
+         Submitted by Chip Salzenberg <chip@valinux.com>
+       - Add call to scsi_set_pci_device() for kernels >= 2.4.4.
+       - Check pci dma mapping failures and complete the IO with some 
+         error when such mapping fails.
+       - Fill in instance->max_cmd_len for kernels > 2.4.0.
+       - A couple of tiny fixes ...
+
+Sun Sep 9 18:00 2001 Gerard Roudier 
+       * version sym-2.1.12-20010909
+       - Change my email address.
+       - Add infrastructure for the forthcoming 64 bit DMA adressing support.
+         (Based on PCI 64 bit patch from David S. Miller)
+       - Donnot use anymore vm_offset_t type.
+
+Sat Sep 15 20:00 2001 Gerard Roudier 
+       * version sym-2.1.13-20010916
+       - Add support for 64 bit DMA addressing using segment registers.
+         16 registers for up to 4 GB x 16 -> 64 GB.
+
+Sat Sep 22 12:00 2001 Gerard Roudier 
+       * version sym-2.1.14-20010922
+       - Complete rewrite of the eh handling. The driver is now using a 
+         semaphore in order to behave synchronously as required by the eh 
+         threads. A timer is also used to prevent from waiting indefinitely.
+
+Sun Sep 30 17:00 2001 Gerard Roudier 
+       * version sym-2.1.15-20010930
+       - Include <linux/module.h> unconditionnaly as expected by latest
+         kernels.
+       - Use del_timer_sync() for recent kernels to kill the driver timer 
+         on module release.
+
+Sun Oct 28 15:00 2001 Gerard Roudier 
+       * version sym-2.1.16-20011028
+       - Slightly simplify driver configuration.
+       - Prepare a new patch against linux-2.4.13.
+
+Sat Nov 17 10:00 2001 Gerard Roudier 
+       * version sym-2.1.17
+       - Fix a couple of gcc/gcc3 warnings.
+       - Allocate separately from the HCB the array for CCBs hashed by DSA.
+         All driver memory allocations are now not greater than 1 PAGE 
+         even on PPC64 / 4KB PAGE surprising setup.
+
+Sat Dec 01 18:00 2001 Gerard Roudier 
+       * version sym-2.1.17a
+       - Use u_long instead of U32 for the IO base cookie. This is more 
+         consistent with what archs are expecting.
+       - Use MMIO per default for Power PC instead of some fake normal IO,
+         as Paul Mackerras stated that MMIO works fine now on this arch.
+
+
+
+
diff --git a/xen/drivers/scsi/sym53c8xx_2/Documentation.txt b/xen/drivers/scsi/sym53c8xx_2/Documentation.txt
new file mode 100644 (file)
index 0000000..713afc8
--- /dev/null
@@ -0,0 +1,1149 @@
+The Linux SYM-2 driver documentation file
+
+Written by Gerard Roudier <groudier@free.fr>
+21 Rue Carnot
+95170 DEUIL LA BARRE - FRANCE
+
+Decembre 28 2000
+===============================================================================
+
+1.  Introduction
+2.  Supported chips and SCSI features
+3.  Advantages of this driver for newer chips.
+      3.1 Optimized SCSI SCRIPTS
+      3.2 New features appeared with the SYM53C896
+4.  Memory mapped I/O versus normal I/O
+5.  Tagged command queueing
+6.  Parity checking
+7.  Profiling information
+8.  Control commands
+      8.1  Set minimum synchronous period
+      8.2  Set wide size
+      8.3  Set maximum number of concurrent tagged commands
+      8.4  Set debug mode
+      8.5  Set flag (no_disc)
+      8.6  Set verbose level
+      8.7  Reset all logical units of a target
+      8.8  Abort all tasks of all logical units of a target
+9.  Configuration parameters
+10. Boot setup commands
+      10.1 Syntax
+      10.2 Available arguments
+             10.2.1  Master parity checking
+             10.2.2  Scsi parity checking
+             10.2.3  Default number of tagged commands
+             10.2.4  Default synchronous period factor
+             10.2.5  Verbosity level
+             10.2.6  Debug mode
+             10.2.7  Burst max
+             10.2.8  LED support
+             10.2.9  Max wide
+             10.2.10 Differential mode
+             10.2.11 IRQ mode
+             10.2.12 Reverse probe
+             10.2.13 Fix up PCI configuration space
+             10.2.14 Serial NVRAM
+             10.2.15 Check SCSI BUS 
+             10.2.16 Exclude a host from being attached
+             10.2.17 Suggest a default SCSI id for hosts
+      10.3 PCI configuration fix-up boot option
+      10.4 Serial NVRAM support boot option
+      10.5 SCSI BUS checking boot option
+11. SCSI problem troubleshooting
+      15.1 Problem tracking
+      15.2 Understanding hardware error reports
+12. Serial NVRAM support (by Richard Waltham)
+      17.1 Features
+      17.2 Symbios NVRAM layout
+      17.3 Tekram  NVRAM layout
+
+===============================================================================
+
+1. Introduction
+
+This driver supports the whole SYM53C8XX family of PCI-SCSI controllers.
+It also support the subset of LSI53C10XX PCI-SCSI controllers that are based 
+on the SYM53C8XX SCRIPTS language.
+
+It replaces the sym53c8xx+ncr53c8xx driver bundle and shares its core code 
+with the FreeBSD SYM-2 driver. The `glue' that allows this driver to work 
+under Linux is contained in 2 files named sym_glue.h and sym_glue.c.
+Other drivers files are intended not to depend on the Operating System 
+on which the driver is used.
+
+The history of this driver can be summerized as follows:
+
+1993: ncr driver written for 386bsd and FreeBSD by:
+          Wolfgang Stanglmeier        <wolf@cologne.de>
+          Stefan Esser                <se@mi.Uni-Koeln.de>
+
+1996: port of the ncr driver to Linux-1.2.13 and rename it ncr53c8xx.
+          Gerard Roudier
+
+1998: new sym53c8xx driver for Linux based on LOAD/STORE instruction and that 
+      adds full support for the 896 but drops support for early NCR devices.
+          Gerard Roudier
+
+1999: port of the sym53c8xx driver to FreeBSD and support for the LSI53C1010 
+      33 MHz and 66MHz Ultra-3 controllers. The new driver is named `sym'.
+          Gerard Roudier
+
+2000: Add support for early NCR devices to FreeBSD `sym' driver.
+      Break the driver into several sources and separate the OS glue 
+      code from the core code that can be shared among different O/Ses.
+      Write a glue code for Linux.
+          Gerard Roudier
+
+This README file addresses the Linux version of the driver. Under FreeBSD, 
+the driver documentation is the sym.8 man page.
+
+Information about new chips is available at LSILOGIC web server:
+
+          http://www.lsilogic.com/
+
+SCSI standard documentations are available at T10 site:
+
+          http://www.t10.org/
+
+Useful SCSI tools written by Eric Youngdale are part of most Linux 
+distributions:
+   scsiinfo:    command line tool
+   scsi-config: TCL/Tk tool using scsiinfo
+
+2. Supported chips and SCSI features
+
+The following features are supported for all chips:
+
+       Synchronous negotiation
+       Disconnection
+       Tagged command queuing
+       SCSI parity checking
+       PCI Master parity checking
+
+Other features depends on chip capabilities.
+The driver notably uses optimized SCRIPTS for devices that support 
+LOAD/STORE and handles PHASE MISMATCH from SCRIPTS for devices that 
+support the corresponding feature.
+
+The following table shows some characteristics of the chip family.
+
+       On board                                   LOAD/STORE   HARDWARE
+Chip   SDMS BIOS   Wide   SCSI std.   Max. sync   SCRIPTS      PHASE MISMATCH
+----   ---------   ----   ---------   ----------  ----------   --------------
+810        N         N      FAST10    10 MB/s        N             N
+810A       N         N      FAST10    10 MB/s        Y             N
+815        Y         N      FAST10    10 MB/s        N             N
+825        Y         Y      FAST10    20 MB/s        N             N
+825A       Y         Y      FAST10    20 MB/s        Y             N
+860        N         N      FAST20    20 MB/s        Y             N
+875        Y         Y      FAST20    40 MB/s        Y             N
+875A       Y         Y      FAST20    40 MB/s        Y             Y
+876        Y         Y      FAST20    40 MB/s        Y             N
+895        Y         Y      FAST40    80 MB/s        Y             N
+895A       Y         Y      FAST40    80 MB/s        Y             Y
+896        Y         Y      FAST40    80 MB/s        Y             Y
+897        Y         Y      FAST40    80 MB/s        Y             Y
+1510D      Y         Y      FAST40    80 MB/s        Y             Y
+1010       Y         Y      FAST80   160 MB/s        Y             Y
+1010_66*   Y         Y      FAST80   160 MB/s        Y             Y
+
+* Chip supports 33MHz and 66MHz PCI bus clock.
+
+
+Summary of other supported features:
+
+Module:                allow to load the driver
+Memory mapped I/O:     increases performance
+Control commands:      write operations to the proc SCSI file system
+Debugging information: written to syslog (expert only)
+Scatter / gather
+Shared interrupt
+Boot setup commands
+Serial NVRAM:          Symbios and Tekram formats
+
+
+3. Advantages of this driver for newer chips.
+
+3.1 Optimized SCSI SCRIPTS.
+
+All chips except the 810, 815 and 825, support new SCSI SCRIPTS instructions 
+named LOAD and STORE that allow to move up to 1 DWORD from/to an IO register 
+to/from memory much faster that the MOVE MEMORY instruction that is supported 
+by the 53c7xx and 53c8xx family.
+
+The LOAD/STORE instructions support absolute and DSA relative addressing 
+modes. The SCSI SCRIPTS had been entirely rewritten using LOAD/STORE instead 
+of MOVE MEMORY instructions.
+
+Due to the lack of LOAD/STORE SCRIPTS instructions by earlier chips, this 
+driver also incorporates a different SCRIPTS set based on MEMORY MOVE, in 
+order to provide support for the entire SYM53C8XX chips family.
+
+3.2 New features appeared with the SYM53C896
+
+Newer chips (see above) allows handling of the phase mismatch context from 
+SCRIPTS (avoids the phase mismatch interrupt that stops the SCSI processor 
+until the C code has saved the context of the transfer).
+
+The 896 and 1010 chips support 64 bit PCI transactions and addressing, 
+while the 895A supports 32 bit PCI transactions and 64 bit addressing.
+The SCRIPTS processor of these chips is not true 64 bit, but uses segment 
+registers for bit 32-63. Another interesting feature is that LOAD/STORE 
+instructions that address the on-chip RAM (8k) remain internal to the chip.
+
+4. Memory mapped I/O versus normal I/O
+
+Memory mapped I/O has less latency than normal I/O and is the recommended 
+way for doing IO with PCI devices. Memory mapped I/O seems to work fine on 
+most hardware configurations, but some poorly designed chipsets may break 
+this feature. A configuration option is provided for normal I/O to be 
+used but the driver defaults to MMIO.
+
+5. Tagged command queueing
+
+Queuing more than 1 command at a time to a device allows it to perform 
+optimizations based on actual head positions and its mechanical 
+characteristics. This feature may also reduce average command latency.
+In order to really gain advantage of this feature, devices must have 
+a reasonnable cache size (No miracle is to be expected for a low-end 
+hard disk with 128 KB or less).
+Some kown old SCSI devices do not properly support tagged command queuing.
+Generally, firmware revisions that fix this kind of problems are available 
+at respective vendor web/ftp sites.
+All I can say is that I never have had problem with tagged queuing using 
+this driver and its predecessors. Hard disks that behaved correctly for 
+me using tagged commands are the following:
+
+- IBM S12 0662
+- Conner 1080S
+- Quantum Atlas I
+- Quantum Atlas II
+- Seagate Cheetah I
+- Quantum Viking II
+- IBM DRVS
+- Quantum Atlas IV
+- Seagate Cheetah II
+
+If your controller has NVRAM, you can configure this feature per target 
+from the user setup tool. The Tekram Setup program allows to tune the 
+maximum number of queued commands up to 32. The Symbios Setup only allows 
+to enable or disable this feature.
+
+The maximum number of simultaneous tagged commands queued to a device
+is currently set to 16 by default.  This value is suitable for most SCSI
+disks.  With large SCSI disks (>= 2GB, cache >= 512KB, average seek time
+<= 10 ms), using a larger value may give better performances.
+
+This driver supports up to 255 commands per device, and but using more than 
+64 is generally not worth-while, unless you are using a very large disk or 
+disk arrays. It is noticeable that most of recent hard disks seem not to 
+accept more than 64 simultaneous commands. So, using more than 64 queued 
+commands is probably just resource wasting.
+
+If your controller does not have NVRAM or if it is managed by the SDMS 
+BIOS/SETUP, you can configure tagged queueing feature and device queue 
+depths from the boot command-line. For example:
+
+  sym53c8xx=tags:4/t2t3q15-t4q7/t1u0q32
+
+will set tagged commands queue depths as follow:
+
+- target 2  all luns  on controller 0 --> 15
+- target 3  all luns  on controller 0 --> 15
+- target 4  all luns  on controller 0 -->  7
+- target 1  lun 0     on controller 1 --> 32
+- all other target/lun                -->  4
+
+In some special conditions, some SCSI disk firmwares may return a
+QUEUE FULL status for a SCSI command. This behaviour is managed by the
+driver using the following heuristic:
+
+- Each time a QUEUE FULL status is returned, tagged queue depth is reduced 
+  to the actual number of disconnected commands. 
+
+- Every 200 successfully completed SCSI commands, if allowed by the
+  current limit, the maximum number of queueable commands is incremented.
+
+Since QUEUE FULL status reception and handling is resource wasting, the 
+driver notifies by default this problem to user by indicating the actual 
+number of commands used and their status, as well as its decision on the 
+device queue depth change.
+The heuristic used by the driver in handling QUEUE FULL ensures that the 
+impact on performances is not too bad. You can get rid of the messages by 
+setting verbose level to zero, as follow:
+
+1st method: boot your system using 'sym53c8xx=verb:0' option.
+2nd method: apply "setverbose 0" control command to the proc fs entry 
+            corresponding to your controller after boot-up.
+
+6. Parity checking
+
+The driver supports SCSI parity checking and PCI bus master parity
+checking. These features must be enabled in order to ensure safe data
+transfers. However, some flawed devices or mother boards will have
+problems with parity. You can disable either PCI parity or SCSI parity 
+checking by entering appropriate options from the boot command line.
+(See 10: Boot setup commands).
+
+7. Profiling information
+
+This driver does not provide profiling informations as did its predecessors.
+This feature was not this useful and added complexity to the code. 
+As the driver code got more complex, I have decided to remove everything 
+that didn't seem actually useful.
+
+8. Control commands
+
+Control commands can be sent to the driver with write operations to
+the proc SCSI file system. The generic command syntax is the
+following:
+
+      echo "<verb> <parameters>" >/proc/scsi/sym53c8xx/0
+      (assumes controller number is 0)
+
+Using "all" for "<target>" parameter with the commands below will
+apply to all targets of the SCSI chain (except the controller).
+
+Available commands:
+
+8.1 Set minimum synchronous period factor
+
+    setsync <target> <period factor>
+
+    target:    target number
+    period:    minimum synchronous period.
+               Maximum speed = 1000/(4*period factor) except for special
+               cases below.
+
+    Specify a period of 0, to force asynchronous transfer mode.
+
+       9 means 12.5 nano-seconds synchronous period
+      10 means 25 nano-seconds synchronous period
+      11 means 30 nano-seconds synchronous period
+      12 means 50 nano-seconds synchronous period
+
+8.2 Set wide size
+
+    setwide <target> <size>
+
+    target:    target number
+    size:      0=8 bits, 1=16bits
+
+8.3 Set maximum number of concurrent tagged commands
+    settags <target> <tags>
+
+    target:    target number
+    tags:      number of concurrent tagged commands
+               must not be greater than configured (default: 16)
+
+8.4 Set debug mode
+
+    setdebug <list of debug flags>
+
+    Available debug flags:
+        alloc:   print info about memory allocations (ccb, lcb)
+        queue:   print info about insertions into the command start queue
+        result:  print sense data on CHECK CONDITION status
+        scatter: print info about the scatter process
+        scripts: print info about the script binding process
+       tiny:    print minimal debugging information
+       timing:  print timing information of the NCR chip
+       nego:    print information about SCSI negotiations
+       phase:   print information on script interruptions
+
+    Use "setdebug" with no argument to reset debug flags.
+
+
+8.5 Set flag (no_disc)
+    setflag <target> <flag>
+
+    target:    target number
+
+    For the moment, only one flag is available:
+
+        no_disc:   not allow target to disconnect.
+
+    Do not specify any flag in order to reset the flag. For example:
+    - setflag 4
+      will reset no_disc flag for target 4, so will allow it disconnections.
+    - setflag all
+      will allow disconnection for all devices on the SCSI bus.
+
+
+8.6 Set verbose level
+
+    setverbose #level
+
+    The driver default verbose level is 1. This command allows to change 
+    th driver verbose level after boot-up.
+
+8.7 Reset all logical units of a target
+
+    resetdev <target>
+
+    target:    target number
+    The driver will try to send a BUS DEVICE RESET message to the target.
+
+8.8 Abort all tasks of all logical units of a target
+
+    cleardev <target>
+
+    target:    target number
+    The driver will try to send a ABORT message to all the logical units 
+    of the target.
+
+
+9. Configuration parameters
+
+Under kernel configuration tools (make menuconfig, for example), it is 
+possible to change some default driver configuration parameters.
+If the firmware of all your devices is perfect enough, all the
+features supported by the driver can be enabled at start-up. However,
+if only one has a flaw for some SCSI feature, you can disable the
+support by the driver of this feature at linux start-up and enable
+this feature after boot-up only for devices that support it safely.
+
+Configuration parameters:
+
+Use normal IO                         (default answer: n)
+    Answer "y" if you suspect your mother board to not allow memory mapped I/O.
+    May slow down performance a little.
+
+Default tagged command queue depth    (default answer: 16)
+    Entering 0 defaults to tagged commands not being used.
+    This parameter can be specified from the boot command line.
+
+Maximum number of queued commands     (default answer: 32)
+    This option allows you to specify the maximum number of tagged commands 
+    that can be queued to a device. The maximum supported value is 255.
+
+Synchronous transfers frequency       (default answer: 80)
+    This option allows you to specify the frequency in MHz the driver 
+    will use at boot time for synchronous data transfer negotiations.
+    0 means "asynchronous data transfers".
+
+10. Boot setup commands
+
+10.1 Syntax
+
+Setup commands can be passed to the driver either at boot time or as a 
+string variable using 'insmod'.
+
+A boot setup command for this driver begins with the driver name "sym53c8xx=".
+The kernel syntax parser then expects an optionnal list of integers separated 
+with comma followed by an optional list of  comma-separated strings.
+
+Example of boot setup command under lilo prompt:
+
+lilo: linux root=/dev/sda2 sym53c8xx=tags:4,sync:10,debug:0x200
+
+- enable tagged commands, up to 4 tagged commands queued.
+- set synchronous negotiation speed to 10 Mega-transfers / second.
+- set DEBUG_NEGO flag.
+
+Since comma seems not to be allowed when defining a string variable using  
+'insmod', the driver also accepts <space> as option separator. 
+The following command will install driver module with the same options as 
+above.
+
+    insmod sym53c8xx.o sym53c8xx="tags:4 sync:10 debug:0x200"
+
+The integer list of arguments is discarded by the driver. 
+
+Each string argument must be specified as "keyword:value". Only lower-case 
+characters and digits are allowed.
+
+10.2 Available arguments
+
+10.2.1  Master parity checking
+        mpar:y     enabled
+        mpar:n     disabled
+
+10.2.2  Scsi parity checking
+        spar:y     enabled
+        spar:n     disabled
+
+10.2.3  Default number of tagged commands
+        tags:0     (or tags:1 ) tagged command queuing disabled
+        tags:#tags (#tags  > 1) tagged command queuing enabled
+  #tags will be truncated to the max queued commands configuration parameter.
+  This option also allows to specify a command queue depth for each device 
+  that support tagged command queueing.
+  Example:
+      sym53c8xx=tags:10/t2t3q16-t5q24/t1u2q32
+               will set devices queue depth as follow:
+      - controller #0 target #2 and target #3                  -> 16 commands,
+      - controller #0 target #5                                -> 24 commands,
+      - controller #1 target #1 logical unit #2                -> 32 commands,
+      - all other logical units (all targets, all controllers) -> 10 commands.
+
+10.2.4  Default synchronous period factor
+        sync:255     disabled (asynchronous transfer mode)
+        sync:#factor
+  #factor =  9     Ultra-3 SCSI 80 Mega-transfers / second (Wide only)
+  #factor = 10     Ultra-2 SCSI 40 Mega-transfers / second
+  #factor = 11     Ultra-2 SCSI 33 Mega-transfers / second
+  #factor < 25     Ultra   SCSI 20 Mega-transfers / second
+  #factor < 50     Fast    SCSI-2
+
+  In all cases, the driver will use the minimum transfer period supported by 
+  controllers according to SYM53C8XX chip type.
+
+10.2.5  Verbosity level
+        verb:0     minimal
+        verb:1     normal
+        verb:2     too much
+
+10.2.6 Debug mode
+        debug:0         clear debug flags
+        debug:#x   set debug flags
+  #x is an integer value combining the following power-of-2 values:
+  DEBUG_ALLOC       0x1
+  DEBUG_PHASE       0x2
+  DEBUG_POLL        0x4
+  DEBUG_QUEUE       0x8
+  DEBUG_RESULT     0x10
+  DEBUG_SCATTER    0x20
+  DEBUG_SCRIPT     0x40
+  DEBUG_TINY       0x80
+  DEBUG_TIMING    0x100
+  DEBUG_NEGO      0x200
+  DEBUG_TAGS      0x400
+  DEBUG_FREEZE    0x800
+  DEBUG_RESTART  0x1000
+
+  You can play safely with DEBUG_NEGO. However, some of these flags may 
+  generate bunches of syslog messages. 
+
+10.2.7 Burst max
+        burst:0    burst disabled
+        burst:255  get burst length from initial IO register settings.
+        burst:#x   burst enabled (1<<#x burst transfers max)
+  #x is an integer value which is log base 2 of the burst transfers max.
+  By default the driver uses the maximum value supported by the chip.
+
+10.2.8 LED support
+        led:1      enable  LED support
+        led:0      disable LED support
+  Donnot enable LED support if your scsi board does not use SDMS BIOS.
+  (See 'Configuration parameters')
+
+10.2.9 Max wide
+        wide:1      wide scsi enabled
+        wide:0      wide scsi disabled
+  Some scsi boards use a 875 (ultra wide) and only supply narrow connectors.
+  If you have connected a wide device with a 50 pins to 68 pins cable 
+  converter, any accepted wide negotiation will break further data transfers.
+  In such a case, using "wide:0" in the bootup command will be helpfull. 
+
+10.2.10 Differential mode
+        diff:0 never set up diff mode
+        diff:1 set up diff mode if BIOS set it
+        diff:2 always set up diff mode
+        diff:3 set diff mode if GPIO3 is not set
+
+10.2.11 IRQ mode
+        irqm:0     always open drain
+        irqm:1     same as initial settings (assumed BIOS settings)
+        irqm:2     always totem pole
+
+10.2.12 Reverse probe
+        revprob:n   probe chip ids from the PCI configuration in this order:
+                    810, 815, 825, 860, 875, 885, 875A, 895, 896, 895A,
+                    1510D, 1010-33, 1010-66.
+        revprob:y   probe chip ids in the reverse order.
+
+10.2.13 Fix up PCI configuration space
+        pcifix:<option bits>
+
+    Available option bits:
+        0x0:   No attempt to fix PCI configuration space registers values.
+        0x1:   Set PCI cache-line size register if not set.
+        0x2:   Set write and invalidate bit in PCI command register.
+
+10.2.14 Serial NVRAM
+        nvram:n     do not look for serial NVRAM
+        nvram:y     test controllers for onboard serial NVRAM
+        (alternate binary form)
+        mvram=<bits options>
+        0x01   look for NVRAM  (equivalent to nvram=y)
+        0x02   ignore NVRAM "Synchronous negotiation" parameters for all devices
+        0x04   ignore NVRAM "Wide negotiation"  parameter for all devices
+        0x08   ignore NVRAM "Scan at boot time" parameter for all devices
+        0x80   also attach controllers set to OFF in the NVRAM (sym53c8xx only)
+
+10.2.15 Check SCSI BUS 
+        buschk:<option bits>
+
+    Available option bits:
+        0x0:   No check.
+        0x1:   Check and donnot attach the controller on error.  
+        0x2:   Check and just warn on error.
+
+10.2.16 Exclude a host from being attached
+        excl=<io_address>
+
+    Prevent host at a given io address from being attached.
+    For example 'sym53c8xx=excl:0xb400,excl:0xc000' indicate to the 
+    driver not to attach hosts at address 0xb400 and 0xc000.
+
+10.2.17 Suggest a default SCSI id for hosts
+        hostid:255     no id suggested.
+        hostid:#x   (0 < x < 7) x suggested for hosts SCSI id.
+
+    If a host SCSI id is available from the NVRAM, the driver will ignore 
+    any value suggested as boot option. Otherwise, if a suggested value 
+    different from 255 has been supplied, it will use it. Otherwise, it will 
+    try to deduce the value previously set in the hardware and use value 
+    7 if the hardware value is zero.
+
+10.3 PCI configuration fix-up boot option
+
+pcifix:<option bits>
+
+Available option bits:
+    0x1:     Set PCI cache-line size register if not set.
+    0x2:     Set write and invalidate bit in PCI command register.
+
+Use 'pcifix:3' in order to allow the driver to fix both PCI features.
+
+Recent SYMBIOS 53C8XX scsi processors are able to use PCI read multiple 
+and PCI write and invalidate commands. These features require the 
+cache line size register to be properly set in the PCI configuration 
+space of the chips. On the other hand, chips will use PCI write and 
+invalidate commands only if the corresponding bit is set to 1 in the 
+PCI command register.
+
+Not all PCI bioses set the PCI cache line register and the PCI write and 
+invalidate bit in the PCI configuration space of 53C8XX chips.
+Optimized PCI accesses may be broken for some PCI/memory controllers or 
+make problems with some PCI boards.
+
+10.4 Serial NVRAM support boot option
+
+nvram:n     do not look for serial NVRAM
+nvram:y     test controllers for onboard serial NVRAM
+
+This option can also been entered as an hexadecimal value that allows 
+to control what information the driver will get from the NVRAM and what 
+information it will ignore.
+For details see '17. Serial NVRAM support'.
+
+When this option is enabled, the driver tries to detect all boards using 
+a Serial NVRAM. This memory is used to hold user set up parameters.
+
+The parameters the driver is able to get from the NVRAM depend on the 
+data format used, as follow:
+
+                                 Tekram format      Symbios format
+General and host parameters
+    Boot order                         N                   Y
+    Host SCSI ID                       Y                   Y
+    SCSI parity checking               Y                   Y
+    Verbose boot messages              N                   Y
+SCSI devices parameters
+    Synchronous transfer speed         Y                   Y
+    Wide 16 / Narrow                   Y                   Y
+    Tagged Command Queuing enabled     Y                   Y
+    Disconnections enabled             Y                   Y
+    Scan at boot time                  N                   Y
+
+In order to speed up the system boot, for each device configured without 
+the "scan at boot time" option, the driver forces an error on the 
+first TEST UNIT READY command received for this device.
+
+Some SDMS BIOS revisions seem to be unable to boot cleanly with very fast 
+hard disks. In such a situation you cannot configure the NVRAM with 
+optimized parameters value.
+
+The 'nvram' boot option can be entered in hexadecimal form in order 
+to ignore some options configured in the NVRAM, as follow:
+
+mvram=<bits options>
+      0x01   look for NVRAM  (equivalent to nvram=y)
+      0x02   ignore NVRAM "Synchronous negotiation" parameters for all devices
+      0x04   ignore NVRAM "Wide negotiation"  parameter for all devices
+      0x08   ignore NVRAM "Scan at boot time" parameter for all devices
+      0x80   also attach controllers set to OFF in the NVRAM (sym53c8xx only)
+
+Option 0x80 is disabled by default.
+Result is that, by default (option not set), the sym53c8xx driver will not 
+attach controllers set to OFF in the NVRAM.
+
+10.5 SCSI BUS checking boot option.
+
+When this option is set to a non-zero value, the driver checks SCSI lines 
+logic state, 100 micro-seconds after having asserted the SCSI RESET line.
+The driver just reads SCSI lines and checks all lines read FALSE except RESET.
+Since SCSI devices shall release the BUS at most 800 nano-seconds after SCSI 
+RESET has been asserted, any signal to TRUE may indicate a SCSI BUS problem.
+Unfortunately, the following common SCSI BUS problems are not detected:
+- Only 1 terminator installed.
+- Misplaced terminators.
+- Bad quality terminators.
+On the other hand, either bad cabling, broken devices, not conformant 
+devices, ... may cause a SCSI signal to be wrong when te driver reads it.
+
+15. SCSI problem troubleshooting
+
+15.1 Problem tracking
+
+Most SCSI problems are due to a non conformant SCSI bus or too buggy
+devices.  If infortunately you have SCSI problems, you can check the
+following things:
+
+- SCSI bus cables
+- terminations at both end of the SCSI chain
+- linux syslog messages (some of them may help you)
+
+If you donnot find the source of problems, you can configure the
+driver or devices in the NVRAM with minimal features.
+
+- only asynchronous data transfers
+- tagged commands disabled
+- disconnections not allowed
+
+Now, if your SCSI bus is ok, your system has every chance to work
+with this safe configuration but performances will not be optimal.
+
+If it still fails, then you can send your problem description to
+appropriate mailing lists or news-groups.  Send me a copy in order to
+be sure I will receive it.  Obviously, a bug in the driver code is
+possible.
+
+  My cyrrent email address: Gerard Roudier <groudier@free.fr>
+
+Allowing disconnections is important if you use several devices on
+your SCSI bus but often causes problems with buggy devices.
+Synchronous data transfers increases throughput of fast devices like
+hard disks.  Good SCSI hard disks with a large cache gain advantage of
+tagged commands queuing.
+
+15.2 Understanding hardware error reports
+
+When the driver detects an unexpected error condition, it may display a 
+message of the following pattern.
+
+sym0:1: ERROR (0:48) (1-21-65) (f/95/0) @ (script 7c0:19000000).
+sym0: script cmd = 19000000
+sym0: regdump: da 10 80 95 47 0f 01 07 75 01 81 21 80 01 09 00.
+
+Some fields in such a message may help you understand the cause of the 
+problem, as follows:
+
+sym0:1: ERROR (0:48) (1-21-65) (f/95/0) @ (script 7c0:19000000).
+.....A.........B.C....D.E..F....G.H..I.......J.....K...L.......
+
+Field A : target number.
+  SCSI ID of the device the controller was talking with at the moment the 
+  error occurs.
+
+Field B : DSTAT io register (DMA STATUS)
+  Bit 0x40 : MDPE Master Data Parity Error
+             Data parity error detected on the PCI BUS.
+  Bit 0x20 : BF   Bus Fault
+             PCI bus fault condition detected
+  Bit 0x01 : IID  Illegal Instruction Detected
+             Set by the chip when it detects an Illegal Instruction format 
+             on some condition that makes an instruction illegal.
+  Bit 0x80 : DFE Dma Fifo Empty
+             Pure status bit that does not indicate an error.
+  If the reported DSTAT value contains a combination of MDPE (0x40), 
+  BF (0x20), then the cause may be likely due to a PCI BUS problem.
+
+Field C : SIST io register (SCSI Interrupt Status)
+  Bit 0x08 : SGE  SCSI GROSS ERROR
+             Indicates that the chip detected a severe error condition 
+             on the SCSI BUS that prevents the SCSI protocol from functionning
+             properly.
+  Bit 0x04 : UDC  Undexpected Disconnection
+             Indicates that the device released the SCSI BUS when the chip 
+             was not expecting this to happen. A device may behave so to 
+             indicate the SCSI initiator that an error condition not reportable              using the SCSI protocol has occured.
+  Bit 0x02 : RST  SCSI BUS Reset
+             Generally SCSI targets donnot reset the SCSI BUS, although any 
+             device on the BUS can reset it at any time.
+  Bit 0x01 : PAR  Parity
+             SCSI parity error detected.
+  On a faulty SCSI BUS, any error condition among SGE (0x08), UDC (0x04) and 
+  PAR (0x01) may be detected by the chip. If your SCSI system sometimes 
+  encounters such error conditions, especially SCSI GROSS ERROR, then a SCSI 
+  BUS problem is likely the cause of these errors.
+
+For fields D,E,F,G and H, you may look into the sym53c8xx_defs.h file 
+that contains some minimal comments on IO register bits.
+Field D : SOCL  Scsi Output Control Latch
+          This register reflects the state of the SCSI control lines the 
+          chip want to drive or compare against.
+Field E : SBCL  Scsi Bus Control Lines
+          Actual value of control lines on the SCSI BUS.
+Field F : SBDL  Scsi Bus Data Lines
+          Actual value of data lines on the SCSI BUS.
+Field G : SXFER  SCSI Transfer
+          Contains the setting of the Synchronous Period for output and 
+          the current Synchronous offset (offset 0 means asynchronous).
+Field H : SCNTL3 Scsi Control Register 3
+          Contains the setting of timing values for both asynchronous and 
+          synchronous data transfers. 
+Field I : SCNTL4 Scsi Control Register 4
+          Only meaninful for 53C1010 Ultra3 controllers.
+
+Understanding Fields J, K, L and dumps requires to have good knowledge of 
+SCSI standards, chip cores functionnals and internal driver data structures.
+You are not required to decode and understand them, unless you want to help 
+maintain the driver code.
+
+17. Serial NVRAM (added by Richard Waltham: dormouse@farsrobt.demon.co.uk)
+
+17.1 Features
+
+Enabling serial NVRAM support enables detection of the serial NVRAM included
+on Symbios and some Symbios compatible host adaptors, and Tekram boards. The 
+serial NVRAM is used by Symbios and Tekram to hold set up parameters for the 
+host adaptor and it's attached drives.
+
+The Symbios NVRAM also holds data on the boot order of host adaptors in a
+system with more than one host adaptor. This enables the order of scanning
+the cards for drives to be changed from the default used during host adaptor
+detection.
+
+This can be done to a limited extent at the moment using "reverse probe" but
+this only changes the order of detection of different types of cards. The
+NVRAM boot order settings can do this as well as change the order the same
+types of cards are scanned in, something "reverse probe" cannot do.
+
+Tekram boards using Symbios chips, DC390W/F/U, which have NVRAM are detected
+and this is used to distinguish between Symbios compatible and Tekram host 
+adaptors. This is used to disable the Symbios compatible "diff" setting
+incorrectly set on Tekram boards if the CONFIG_SCSI_53C8XX_SYMBIOS_COMPAT 
+configuration parameter is set enabling both Symbios and Tekram boards to be 
+used together with the Symbios cards using all their features, including
+"diff" support. ("led pin" support for Symbios compatible cards can remain
+enabled when using Tekram cards. It does nothing useful for Tekram host
+adaptors but does not cause problems either.)
+
+
+17.2 Symbios NVRAM layout
+
+typical data at NVRAM address 0x100 (53c810a NVRAM)
+-----------------------------------------------------------
+00 00
+64 01
+8e 0b
+
+00 30 00 00 00 00 07 00 00 00 00 00 00 00 07 04 10 04 00 00 
+
+04 00 0f 00 00 10 00 50 00 00 01 00 00 62 
+04 00 03 00 00 10 00 58 00 00 01 00 00 63 
+04 00 01 00 00 10 00 48 00 00 01 00 00 61 
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 
+
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+
+fe fe
+00 00
+00 00
+-----------------------------------------------------------
+NVRAM layout details
+
+NVRAM Address 0x000-0x0ff not used
+              0x100-0x26f initialised data
+              0x270-0x7ff not used
+
+general layout
+
+        header  -   6 bytes,
+        data    - 356 bytes (checksum is byte sum of this data)
+        trailer -   6 bytes
+                  ---
+        total     368 bytes
+
+data area layout
+
+        controller set up  -  20 bytes
+        boot configuration -  56 bytes (4x14 bytes)
+        device set up      - 128 bytes (16x8 bytes)
+        unused (spare?)    - 152 bytes (19x8 bytes)
+                             ---
+        total                356 bytes
+
+-----------------------------------------------------------
+header
+
+00 00   - ?? start marker
+64 01   - byte count (lsb/msb excludes header/trailer)
+8e 0b   - checksum (lsb/msb excludes header/trailer)
+-----------------------------------------------------------
+controller set up
+
+00 30 00 00 00 00 07 00 00 00 00 00 00 00 07 04 10 04 00 00
+                   |     |           |     |
+                   |     |           |      -- host ID
+                   |     |           |
+                   |     |            --Removable Media Support
+                   |     |               0x00 = none
+                   |     |               0x01 = Bootable Device
+                   |     |               0x02 = All with Media
+                   |     |
+                   |      --flag bits 2
+                   |        0x00000001= scan order hi->low
+                   |            (default 0x00 - scan low->hi)
+                    --flag bits 1
+                       0x00000001 scam enable
+                       0x00000010 parity enable
+                       0x00000100 verbose boot msgs
+
+remaining bytes unknown - they do not appear to change in my
+current set up for any of the controllers.
+
+default set up is identical for 53c810a and 53c875 NVRAM
+(Removable Media added Symbios BIOS version 4.09)
+-----------------------------------------------------------
+boot configuration
+
+boot order set by order of the devices in this table
+
+04 00 0f 00 00 10 00 50 00 00 01 00 00 62 -- 1st controller
+04 00 03 00 00 10 00 58 00 00 01 00 00 63    2nd controller
+04 00 01 00 00 10 00 48 00 00 01 00 00 61    3rd controller
+00 00 00 00 00 00 00 00 00 00 00 00 00 00    4th controller
+       |  |  |  |     |        |     |  |
+       |  |  |  |     |        |      ---- PCI io port adr
+       |  |  |  |     |         --0x01 init/scan at boot time
+       |  |  |  |      --PCI device/function number (0xdddddfff)
+       |  |   ----- ?? PCI vendor ID (lsb/msb)
+        ----PCI device ID (lsb/msb)
+
+?? use of this data is a guess but seems reasonable
+
+remaining bytes unknown - they do not appear to change in my
+current set up
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+device set up (up to 16 devices - includes controller)
+
+0f 00 08 08 64 00 0a 00 - id 0
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 - id 15
+ |     |  |  |     |  |
+ |     |  |  |      ----timeout (lsb/msb)
+ |     |  |   --synch period (0x?? 40 Mtrans/sec- fast 40) (probably 0x28)
+ |     |  |                  (0x30 20 Mtrans/sec- fast 20)
+ |     |  |                  (0x64 10 Mtrans/sec- fast )
+ |     |  |                  (0xc8  5 Mtrans/sec)
+ |     |  |                  (0x00  asynchronous)
+ |     |   -- ?? max sync offset (0x08 in NVRAM on 53c810a) 
+ |     |                         (0x10 in NVRAM on 53c875)
+ |      --device bus width (0x08 narrow)
+ |                         (0x10 16 bit wide)
+  --flag bits
+    0x00000001 - disconnect enabled
+    0x00000010 - scan at boot time
+    0x00000100 - scan luns
+    0x00001000 - queue tags enabled
+
+remaining bytes unknown - they do not appear to change in my
+current set up
+
+?? use of this data is a guess but seems reasonable 
+(but it could be max bus width)
+
+default set up for 53c810a NVRAM
+default set up for 53c875 NVRAM - bus width     - 0x10
+                                - sync offset ? - 0x10
+                                - sync period   - 0x30
+-----------------------------------------------------------
+?? spare device space (32 bit bus ??)
+
+00 00 00 00 00 00 00 00  (19x8bytes)
+.
+.
+00 00 00 00 00 00 00 00
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+trailer
+
+fe fe   - ? end marker ?
+00 00
+00 00
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+
+
+
+17.3 Tekram NVRAM layout
+
+nvram 64x16 (1024 bit)
+
+Drive settings
+
+Drive ID 0-15 (addr 0x0yyyy0 = device setup, yyyy = ID)
+              (addr 0x0yyyy1 = 0x0000)
+
+    x x x x  x x x x  x x x x  x x x x
+               | | |      | |  | | | |
+               | | |      | |  | | |  ----- parity check   0 - off
+               | | |      | |  | | |                       1 - on
+               | | |      | |  | | |
+               | | |      | |  | |  ------- sync neg       0 - off
+               | | |      | |  | |                         1 - on
+               | | |      | |  | |
+               | | |      | |  |  --------- disconnect     0 - off
+               | | |      | |  |                           1 - on
+               | | |      | |  |
+               | | |      | |   ----------- start cmd      0 - off
+               | | |      | |                              1 - on
+               | | |      | |
+               | | |      |  -------------- tagged cmds    0 - off
+               | | |      |                                1 - on
+               | | |      | 
+               | | |       ---------------- wide neg       0 - off
+               | | |                                       1 - on
+               | | |
+                --------------------------- sync rate      0 - 10.0 Mtrans/sec
+                                                           1 -  8.0
+                                                           2 -  6.6
+                                                           3 -  5.7
+                                                           4 -  5.0
+                                                           5 -  4.0
+                                                           6 -  3.0
+                                                           7 -  2.0
+                                                           7 -  2.0
+                                                           8 - 20.0
+                                                           9 - 16.7
+                                                           a - 13.9
+                                                           b - 11.9
+
+Global settings
+
+Host flags 0 (addr 0x100000, 32) 
+
+    x x x x  x x x x  x x x x  x x x x
+    | | | |  | | | |           | | | |
+    | | | |  | | | |            ----------- host ID    0x00 - 0x0f
+    | | | |  | | | |
+    | | | |  | | |  ----------------------- support for    0 - off
+    | | | |  | | |                          > 2 drives     1 - on
+    | | | |  | | | 
+    | | | |  | |  ------------------------- support drives 0 - off
+    | | | |  | |                            > 1Gbytes      1 - on
+    | | | |  | |
+    | | | |  |  --------------------------- bus reset on   0 - off
+    | | | |  |                                power on     1 - on
+    | | | |  |
+    | | | |   ----------------------------- active neg     0 - off
+    | | | |                                                1 - on
+    | | | |
+    | | |  -------------------------------- imm seek       0 - off
+    | | |                                                  1 - on
+    | | |
+    | |  ---------------------------------- scan luns      0 - off
+    | |                                                    1 - on
+    | |
+     -------------------------------------- removable      0 - disable
+                                            as BIOS dev    1 - boot device
+                                                           2 - all
+
+Host flags 1 (addr 0x100001, 33)
+
+    x x x x  x x x x  x x x x  x x x x
+               | | |             | | | 
+               | | |              --------- boot delay     0 -   3 sec
+               | | |                                       1 -   5
+               | | |                                       2 -  10
+               | | |                                       3 -  20
+               | | |                                       4 -  30
+               | | |                                       5 -  60
+               | | |                                       6 - 120
+               | | |
+                --------------------------- max tag cmds   0 -  2
+                                                           1 -  4
+                                                           2 -  8
+                                                           3 - 16
+                                                           4 - 32
+
+Host flags 2 (addr 0x100010, 34)
+
+    x x x x  x x x x  x x x x  x x x x
+                                     |
+                                      ----- F2/F6 enable   0 - off ???
+                                                           1 - on  ???
+
+checksum (addr 0x111111)
+
+checksum = 0x1234 - (sum addr 0-63)
+
+----------------------------------------------------------------------------
+
+default nvram data:
+
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 
+
+0x0f07 0x0400 0x0001 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0xfbbc
+
+
+===============================================================================
+End of Linux SYM-2 driver documentation file
diff --git a/xen/drivers/scsi/sym53c8xx_2/Makefile b/xen/drivers/scsi/sym53c8xx_2/Makefile
new file mode 100644 (file)
index 0000000..c03905f
--- /dev/null
@@ -0,0 +1,18 @@
+# File: drivers/sym53c8xx/Makefile
+# Makefile for the NCR/SYMBIOS/LSI 53C8XX PCI SCSI controllers driver.
+
+O_TARGET := sym53c8xx_drv.o
+#list-multi := sym53c8xx.o
+sym53c8xx-objs := sym_fw.o sym_glue.o sym_hipd.o sym_malloc.o sym_misc.o sym_nvram.o
+obj-sym53c8xx := sym53c8xx.o
+#obj-$(CONFIG_SCSI_SYM53C8XX_2) := sym53c8xx.o
+
+EXTRA_CFLAGS += -I.
+
+sym53c8xx.o: $(sym53c8xx-objs)
+       $(LD) -r -o $@ $(sym53c8xx-objs)
+
+include $(BASEDIR)/Rules.mk
+
+clean:
+       rm -f *.o
diff --git a/xen/drivers/scsi/sym53c8xx_2/sym53c8xx.h b/xen/drivers/scsi/sym53c8xx_2/sym53c8xx.h
new file mode 100644 (file)
index 0000000..73bd0ed
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000  Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ *         Wolfgang Stanglmeier        <wolf@cologne.de>
+ *         Stefan Esser                <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994  Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef SYM53C8XX_H
+#define SYM53C8XX_H
+
+#if !defined(LINUX_VERSION_CODE)
+#include <xeno/version.h>
+#endif
+#include <xeno/config.h>
+
+/*
+ *  Compatibility with ncr53c8xx and sym53c8xx configuration options.
+ */
+#ifndef        CONFIG_SCSI_SYM53C8XX_IOMAPPED
+#ifdef CONFIG_SCSI_NCR53C8XX_IOMAPPED
+#define        CONFIG_SCSI_SYM53C8XX_IOMAPPED          CONFIG_SCSI_NCR53C8XX_IOMAPPED
+#endif
+#endif
+
+#ifndef        CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS
+#ifdef CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS
+#define        CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS      CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS
+#endif
+#endif
+
+#ifndef        CONFIG_SCSI_SYM53C8XX_MAX_TAGS
+#ifdef CONFIG_SCSI_NCR53C8XX_MAX_TAGS
+#define        CONFIG_SCSI_SYM53C8XX_MAX_TAGS          CONFIG_SCSI_NCR53C8XX_MAX_TAGS
+#endif
+#endif
+
+int sym53c8xx_detect(Scsi_Host_Template *tpnt);
+const char *sym53c8xx_info(struct Scsi_Host *host);
+
+int sym53c8xx_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+
+int sym53c8xx_eh_abort_handler(Scsi_Cmnd *);
+int sym53c8xx_eh_device_reset_handler(Scsi_Cmnd *);
+int sym53c8xx_eh_bus_reset_handler(Scsi_Cmnd *);
+int sym53c8xx_eh_host_reset_handler(Scsi_Cmnd *);
+
+#ifdef MODULE
+int sym53c8xx_release(struct Scsi_Host *);
+#else
+#define sym53c8xx_release NULL
+#endif
+
+
+/*
+ *  Host template defintion
+ */
+#if (LINUX_VERSION_CODE >= 0x020400) || defined(HOSTS_C) || defined(MODULE)
+
+#include <scsi/scsicam.h>
+
+#define SYM53C8XX {                                                    \
+       name:                   "sym53c8xx",                            \
+       detect:                 sym53c8xx_detect,                       \
+       release:                sym53c8xx_release,                      \
+       info:                   sym53c8xx_info,                         \
+       queuecommand:           sym53c8xx_queue_command,                \
+       use_new_eh_code:        1,                                      \
+       eh_abort_handler:       sym53c8xx_eh_abort_handler,             \
+       eh_device_reset_handler:sym53c8xx_eh_device_reset_handler,      \
+       eh_bus_reset_handler:   sym53c8xx_eh_bus_reset_handler,         \
+       eh_host_reset_handler:  sym53c8xx_eh_host_reset_handler,        \
+       bios_param:             scsicam_bios_param,                     \
+       can_queue:              0,                                      \
+       this_id:                7,                                      \
+       sg_tablesize:           0,                                      \
+       cmd_per_lun:            0,                                      \
+       use_clustering:         DISABLE_CLUSTERING,                     \
+       }
+/* SAE */
+#ifdef XENO_KILLED
+       highmem_io:             1}
+#endif
+
+#endif /* defined(HOSTS_C) || defined(MODULE) */ 
+
+/*
+ *  Translate kernel configuration parameters
+ *  into corresponding driver parameters.
+ */
+#if !defined(HOSTS_C)
+
+/*
+ *  Use normal IO if configured.
+ *  Normal IO forced for alpha.
+ *  Forced to MMIO for sparc.
+ */
+#if defined(__alpha__)
+#define        SYM_CONF_IOMAPPED
+#elif defined(__sparc__)
+#undef SYM_CONF_IOMAPPED
+/* #elif defined(__powerpc__) */
+/* #define     SYM_CONF_IOMAPPED */
+/* #define SYM_OPT_NO_BUS_MEMORY_MAPPING */
+#elif defined(CONFIG_SCSI_SYM53C8XX_IOMAPPED)
+#define        SYM_CONF_IOMAPPED
+#endif
+
+/*
+ *  DMA addressing mode.
+ *
+ *  0 : 32 bit addressing for all chips.
+ *  1 : 40 bit addressing when supported by chip.
+ *  2 : 64 bit addressing when supported by chip,
+ *      limited to 16 segments of 4 GB -> 64 GB max.
+ */
+#ifdef CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE
+#define        SYM_CONF_DMA_ADDRESSING_MODE CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE
+#endif
+
+/*
+ *  NCR PQS/PDS special device support.
+ */
+#if 1
+#define SYM_CONF_PQS_PDS_SUPPORT
+#endif
+
+/*
+ *  NVRAM support.
+ */
+#if 1
+#define SYM_CONF_NVRAM_SUPPORT         (1)
+#define SYM_SETUP_SYMBIOS_NVRAM                (1)
+#define SYM_SETUP_TEKRAM_NVRAM         (1)
+#endif
+
+/*
+ *  These options are not tunable from 'make config'
+ */
+#if 1
+#define        SYM_LINUX_PROC_INFO_SUPPORT
+#define SYM_LINUX_BOOT_COMMAND_LINE_SUPPORT
+#define SYM_LINUX_USER_COMMAND_SUPPORT
+#define SYM_LINUX_USER_INFO_SUPPORT
+#define SYM_LINUX_DEBUG_CONTROL_SUPPORT
+#endif
+
+/*
+ *  Also handle old NCR chips if not (0).
+ */
+#define SYM_CONF_GENERIC_SUPPORT       (1)
+
+/*
+ *  Allow tags from 2 to 256, default 8
+ */
+#ifndef CONFIG_SCSI_SYM53C8XX_MAX_TAGS
+#define CONFIG_SCSI_SYM53C8XX_MAX_TAGS (8)
+#endif
+
+#if    CONFIG_SCSI_SYM53C8XX_MAX_TAGS < 2
+#define SYM_CONF_MAX_TAG       (2)
+#elif  CONFIG_SCSI_SYM53C8XX_MAX_TAGS > 256
+#define SYM_CONF_MAX_TAG       (256)
+#else
+#define        SYM_CONF_MAX_TAG        CONFIG_SCSI_SYM53C8XX_MAX_TAGS
+#endif
+
+#ifndef        CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS
+#define        CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS      SYM_CONF_MAX_TAG
+#endif
+
+/*
+ *  Anyway, we configure the driver for at least 64 tags per LUN. :)
+ */
+#if    SYM_CONF_MAX_TAG <= 64
+#define SYM_CONF_MAX_TAG_ORDER (6)
+#elif  SYM_CONF_MAX_TAG <= 128
+#define SYM_CONF_MAX_TAG_ORDER (7)
+#else
+#define SYM_CONF_MAX_TAG_ORDER (8)
+#endif
+
+/*
+ *  Sync transfer frequency at startup.
+ *  Allow up to ULTRA-160. The driver will scale the value 
+ *  according to controller capabilities.
+ */
+#define        CONFIG_SCSI_SYM53C8XX_DEFAULT_SYNC (9)
+
+/*
+ *  Max number of SG entries.
+ */
+#define SYM_CONF_MAX_SG                (96)
+
+/*
+ *  Max number of LUNs per target.
+ */
+#if 1 /* defined CONFIG_SCSI_MULTI_LUN */
+#define        CONFIG_SCSI_SYM53C8XX_MAX_LUN   (16)
+#else
+#define        CONFIG_SCSI_SYM53C8XX_MAX_LUN   (1)
+#endif
+
+/*
+ *  Driver setup structure.
+ *
+ *  This structure is initialized from linux config options.
+ *  It can be overridden at boot-up by the boot command line.
+ */
+struct sym_driver_setup {
+       u_char  pci_parity;
+       u_char  scsi_parity;
+       u_short max_tag;
+       u_char  min_sync;
+       u_char  burst_order;
+       u_char  scsi_led;
+       u_char  max_wide;
+       u_char  scsi_diff;
+       u_char  irq_mode;
+       u_char  scsi_bus_check;
+       u_char  host_id;
+       u_char  max_offs;
+       u_char  max_lun;
+       u_char  pci_fix_up;
+
+       u_char  reverse_probe;
+       u_char  verbose;
+       u_short debug;
+       u_char  settle_delay;
+       u_char  use_nvram;
+       u_long  excludes[8];
+       char    tag_ctrl[100];
+};
+
+#define SYM_SETUP_PCI_PARITY           sym_driver_setup.pci_parity
+#define SYM_SETUP_SCSI_PARITY          sym_driver_setup.scsi_parity
+#define SYM_SETUP_MAX_TAG              sym_driver_setup.max_tag
+#define SYM_SETUP_MIN_SYNC             sym_driver_setup.min_sync
+#define SYM_SETUP_BURST_ORDER          sym_driver_setup.burst_order
+#define SYM_SETUP_SCSI_LED             sym_driver_setup.scsi_led
+#define SYM_SETUP_MAX_WIDE             sym_driver_setup.max_wide
+#define SYM_SETUP_SCSI_DIFF            sym_driver_setup.scsi_diff
+#define SYM_SETUP_IRQ_MODE             sym_driver_setup.irq_mode
+#define SYM_SETUP_SCSI_BUS_CHECK       sym_driver_setup.scsi_bus_check
+#define SYM_SETUP_HOST_ID              sym_driver_setup.host_id
+#define SYM_SETUP_MAX_OFFS             sym_driver_setup.max_offs
+#define SYM_SETUP_MAX_LUN              sym_driver_setup.max_lun
+#define SYM_SETUP_PCI_FIX_UP           sym_driver_setup.pci_fix_up
+
+/*
+ *  Initial setup.
+ *
+ *  Can be overriden at startup by a command line.
+ */
+#define SYM_LINUX_DRIVER_SETUP                 \
+{                                              \
+       1,      /* pci_parity */                \
+       1,      /* scsi_parity */               \
+       CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS,     \
+       CONFIG_SCSI_SYM53C8XX_DEFAULT_SYNC,     \
+       7,      /* burst_order */               \
+       1,      /* scsi_led */                  \
+       1,      /* max_wide */                  \
+       1,      /* scsi_diff */                 \
+       0,      /* irq_mode */                  \
+       1,      /* scsi_bus_check */            \
+       7,      /* host_id */                   \
+       62,     /* max_offs */                  \
+       CONFIG_SCSI_SYM53C8XX_MAX_LUN,          \
+       3,      /* pci_fix_up */                \
+       0,      /* reverse_probe */             \
+       0,      /* verbose */                   \
+       0,      /* debug */                     \
+       3,      /* settle_delay */              \
+       1,      /* use_nvram */                 \
+}
+
+/*
+ *  Boot fail safe setup.
+ *
+ *  Override initial setup from boot command line:
+ *    sym53c8xx=safe:y
+ */
+#define SYM_LINUX_DRIVER_SAFE_SETUP            \
+{                                              \
+       0,      /* pci_parity */                \
+       0,      /* scsi_parity */               \
+       0,      /* max_tag */                   \
+       50,     /* min_sync */                  \
+       0,      /* burst_order */               \
+       0,      /* scsi_led */                  \
+       1,      /* max_wide */                  \
+       1,      /* scsi_diff */                 \
+       0,      /* irq_mode */                  \
+       2,      /* scsi_bus_check */            \
+       7,      /* host_id */                   \
+       15,     /* max_offs */                  \
+       1,      /* max_lun */                   \
+       0,      /* pci_fix_up */                \
+       0,      /* reverse_probe */             \
+       2,      /* verbose */                   \
+       0,      /* debug */                     \
+       10,     /* settle_delay */              \
+       1,      /* use_nvram */                 \
+}
+
+/*
+ *  This structure is initialized from linux config options.
+ *  It can be overridden at boot-up by the boot command line.
+ */
+#ifdef SYM_GLUE_C
+struct sym_driver_setup
+       sym_driver_setup = SYM_LINUX_DRIVER_SETUP;
+#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT
+u_int  sym_debug_flags = 0;
+#endif
+#else
+extern struct sym_driver_setup sym_driver_setup;
+#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT
+extern u_int sym_debug_flags;
+#endif
+#endif /* SYM_GLUE_C */
+
+#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT
+#define DEBUG_FLAGS    sym_debug_flags
+#endif
+#define boot_verbose   sym_driver_setup.verbose
+
+#endif /* !defined(HOSTS_C) */ 
+
+#endif /* SYM53C8XX_H */
diff --git a/xen/drivers/scsi/sym53c8xx_2/sym_conf.h b/xen/drivers/scsi/sym53c8xx_2/sym_conf.h
new file mode 100644 (file)
index 0000000..0877411
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000  Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ *         Wolfgang Stanglmeier        <wolf@cologne.de>
+ *         Stefan Esser                <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994  Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef SYM_CONF_H
+#define SYM_CONF_H
+
+/*-------------------------------------------------------------------
+ *  Static configuration.
+ *-------------------------------------------------------------------
+ */
+
+/*
+ *  Also support early NCR 810, 815 and 825 chips.
+ */
+#ifndef SYM_CONF_GENERIC_SUPPORT
+#define SYM_CONF_GENERIC_SUPPORT       (1)
+#endif
+
+/*
+ *  Use Normal IO instead of MMIO.
+ */
+/* #define SYM_CONF_IOMAPPED */
+
+/*
+ *  Max tags for a device (logical unit)
+ *     We use a power of 2, (7) means 2<<7=128
+ *  Maximum is 8 -> 256 tags
+ */
+#ifndef SYM_CONF_MAX_TAG_ORDER
+#define SYM_CONF_MAX_TAG_ORDER (6)
+#endif
+
+/*
+ *  Max number of scatter/gather entries for en IO.
+ *  Each entry costs 8 bytes in the internal CCB data structure.
+ */
+#ifndef SYM_CONF_MAX_SG
+#define SYM_CONF_MAX_SG                (33)
+#endif
+
+/*
+ *  Max number of targets.
+ *  Maximum is 16 and you are advised not to change this value.
+ */
+#ifndef SYM_CONF_MAX_TARGET
+#define SYM_CONF_MAX_TARGET    (16)
+#endif
+
+/*
+ *  Max number of logical units.
+ *  SPI-2 allows up to 64 logical units, but in real life, target
+ *  that implements more that 7 logical units are pretty rare.
+ *  Anyway, the cost of accepting up to 64 logical unit is low in 
+ *  this driver, thus going with the maximum is acceptable.
+ */
+#ifndef SYM_CONF_MAX_LUN
+#define SYM_CONF_MAX_LUN       (64)
+#endif
+
+/*
+ *  Max number of IO control blocks queued to the controller.
+ *  Each entry needs 8 bytes and the queues are allocated contiguously.
+ *  Since we donnot want to allocate more than a page, the theorical 
+ *  maximum is PAGE_SIZE/8. For safety, we announce a bit less to the 
+ *  access method. :)
+ *  When not supplied, as it is suggested, the driver compute some 
+ *  good value for this parameter.
+ */
+/* #define SYM_CONF_MAX_START  (PAGE_SIZE/8 - 16) */
+
+/*
+ *  Support for NVRAM.
+ */
+#ifndef SYM_CONF_NVRAM_SUPPORT
+#define SYM_CONF_NVRAM_SUPPORT         (1)
+#endif
+
+/*
+ *  Support for Immediate Arbitration.
+ *  Not advised.
+ */
+/* #define SYM_CONF_IARB_SUPPORT */
+
+/*
+ *  Support for some PCI fix-ups (or assumed so).
+ */
+#define SYM_CONF_PCI_FIX_UP
+
+/*
+ *  Number of lists for the optimization of the IO timeout handling.
+ *  Not used under FreeBSD and Linux.
+ */
+#ifndef SYM_CONF_TIMEOUT_ORDER_MAX
+#define SYM_CONF_TIMEOUT_ORDER_MAX     (8)
+#endif
+
+/*
+ *  How the driver handles DMA addressing of user data.
+ *  0 :        32 bit addressing
+ *  1 :        40 bit addressing
+ *  2 :        64 bit addressing using segment registers
+ */
+#ifndef SYM_CONF_DMA_ADDRESSING_MODE
+#define SYM_CONF_DMA_ADDRESSING_MODE   (0)
+#endif
+
+/*-------------------------------------------------------------------
+ *  Configuration that could be dynamic if it was possible 
+ *  to pass arguments to the driver.
+ *-------------------------------------------------------------------
+ */
+
+/*
+ *  HOST default scsi id.
+ */
+#ifndef SYM_SETUP_HOST_ID
+#define SYM_SETUP_HOST_ID      7
+#endif
+
+/*
+ *  Max synchronous transfers.
+ */
+#ifndef SYM_SETUP_MIN_SYNC
+#define SYM_SETUP_MIN_SYNC     (9)
+#endif
+
+/*
+ *  Max wide order.
+ */
+#ifndef SYM_SETUP_MAX_WIDE
+#define SYM_SETUP_MAX_WIDE     (1)
+#endif
+
+/*
+ *  Max SCSI offset.
+ */
+#ifndef SYM_SETUP_MAX_OFFS
+#define SYM_SETUP_MAX_OFFS     (63)
+#endif
+
+/*
+ *  Default number of tags.
+ */
+#ifndef SYM_SETUP_MAX_TAG
+#define SYM_SETUP_MAX_TAG      (1<<SYM_CONF_MAX_TAG_ORDER)
+#endif
+
+/*
+ *  SYMBIOS NVRAM format support.
+ */
+#ifndef SYM_SETUP_SYMBIOS_NVRAM
+#define SYM_SETUP_SYMBIOS_NVRAM        (1)
+#endif
+
+/*
+ *  TEKRAM NVRAM format support.
+ */
+#ifndef SYM_SETUP_TEKRAM_NVRAM
+#define SYM_SETUP_TEKRAM_NVRAM (1)
+#endif
+
+/*
+ *  PCI parity checking.
+ *  It should not be an option, but some poor or broken 
+ *  PCI-HOST bridges have been reported to make problems 
+ *  when this feature is enabled.
+ *  Setting this option to 0 tells the driver not to 
+ *  enable the checking against PCI parity.
+ */
+#ifndef SYM_SETUP_PCI_PARITY
+#define SYM_SETUP_PCI_PARITY   (2)
+#endif
+
+/*
+ *  SCSI parity checking.
+ */
+#ifndef SYM_SETUP_SCSI_PARITY
+#define SYM_SETUP_SCSI_PARITY  (1)
+#endif
+
+/*
+ *  SCSI activity LED.
+ */
+#ifndef SYM_SETUP_SCSI_LED
+#define SYM_SETUP_SCSI_LED     (0)
+#endif
+
+/*
+ *  SCSI High Voltage Differential support.
+ *
+ *  HVD/LVD/SE capable controllers (895, 895A, 896, 1010) 
+ *  report the actual SCSI BUS mode from the STEST4 IO 
+ *  register.
+ *
+ *  But for HVD/SE only capable chips (825a, 875, 885), 
+ *  the driver uses some heuristic to probe against HVD. 
+ *  Normally, the chip senses the DIFFSENS signal and 
+ *  should switch its BUS tranceivers to high impedance 
+ *  in situation of the driver having been wrong about 
+ *  the actual BUS mode. May-be, the BUS mode probing of 
+ *  the driver is safe, but, given that it may be partially 
+ *  based on some previous IO register settings, it 
+ *  cannot be stated so. Thus, decision has been taken 
+ *  to require a user option to be set for the DIFF probing 
+ *  to be applied for the 825a, 875 and 885 chips.
+ *  
+ *  This setup option works as follows:
+ *
+ *    0  ->  HVD only supported for 895, 895A, 896, 1010.
+ *    1  ->  HVD probed  for 825A, 875, 885.
+ *    2  ->  HVD assumed for 825A, 875, 885 (not advised).
+ */
+#ifndef SYM_SETUP_SCSI_DIFF
+#define SYM_SETUP_SCSI_DIFF    (0)
+#endif
+
+/*
+ *  IRQ mode.
+ */
+#ifndef SYM_SETUP_IRQ_MODE
+#define SYM_SETUP_IRQ_MODE     (0)
+#endif
+
+/*
+ *  Check SCSI BUS signal on reset.
+ */
+#ifndef SYM_SETUP_SCSI_BUS_CHECK
+#define SYM_SETUP_SCSI_BUS_CHECK (1)
+#endif
+
+/*
+ *  Max burst for PCI (1<<value)
+ *  7 means: (1<<7) = 128 DWORDS.
+ */
+#ifndef SYM_SETUP_BURST_ORDER
+#define SYM_SETUP_BURST_ORDER  (7)
+#endif
+
+/*
+ *  Only relevant if IARB support configured.
+ *  - Max number of successive settings of IARB hints.
+ *  - Set IARB on arbitration lost.
+ */
+#define SYM_CONF_IARB_MAX 3
+#define SYM_CONF_SET_IARB_ON_ARB_LOST 1
+
+/*
+ *  Returning wrong residuals may make problems.
+ *  When zero, this define tells the driver to 
+ *  always return 0 as transfer residual.
+ *  Btw, all my testings of residuals have succeeded.
+ */
+#define SYM_SETUP_RESIDUAL_SUPPORT 1
+
+/*
+ *  Supported maximum number of LUNs to announce to 
+ *  the access method.
+ *  The driver supports up to 64 LUNs per target as 
+ *  required by SPI-2/SPI-3. However some SCSI devices  
+ *  designed prior to these specifications or not being  
+ *  conformant may be highly confused when they are 
+ *  asked about a LUN > 7.
+ */
+#ifndef SYM_SETUP_MAX_LUN
+#define SYM_SETUP_MAX_LUN      (8)
+#endif
+
+/*
+ *  Bits indicating what kind of fix-ups we want.
+ *
+ *  Bit 0 (1) : cache line size configuration register.
+ *  Bit 1 (2) : MWI bit in command register.
+ *  Bit 2 (4) : latency timer if seems too low.
+ */
+
+#ifndef SYM_SETUP_PCI_FIX_UP
+#define SYM_SETUP_PCI_FIX_UP (3)
+#endif
+
+#endif /* SYM_CONF_H */
diff --git a/xen/drivers/scsi/sym53c8xx_2/sym_defs.h b/xen/drivers/scsi/sym53c8xx_2/sym_defs.h
new file mode 100644 (file)
index 0000000..70649d1
--- /dev/null
@@ -0,0 +1,957 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000  Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ *         Wolfgang Stanglmeier        <wolf@cologne.de>
+ *         Stefan Esser                <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994  Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef SYM_DEFS_H
+#define SYM_DEFS_H
+
+/*
+ *  Vendor.
+ */
+#define PCI_VENDOR_NCR         0x1000
+
+/*
+ *  PCI device identifier of SYMBIOS chips.
+ */
+#define PCI_ID_SYM53C810       1
+#define PCI_ID_SYM53C810AP     5
+#define PCI_ID_SYM53C815       4
+#define PCI_ID_SYM53C820       2
+#define PCI_ID_SYM53C825       3
+#define PCI_ID_SYM53C860       6
+#define PCI_ID_SYM53C875       0xf
+#define PCI_ID_SYM53C875_2     0x8f
+#define PCI_ID_SYM53C885       0xd
+#define PCI_ID_SYM53C895       0xc
+#define PCI_ID_SYM53C896       0xb
+#define PCI_ID_SYM53C895A      0x12
+#define PCI_ID_SYM53C875A      0x13
+#define PCI_ID_LSI53C1010      0x20
+#define PCI_ID_LSI53C1010_2    0x21
+#define PCI_ID_LSI53C1510D     0xa
+
+/*
+ *     SYM53C8XX device features descriptor.
+ */
+struct sym_pci_chip {
+       u_short device_id;
+       u_short revision_id;
+       char    *name;
+       u_char  burst_max;      /* log-base-2 of max burst */
+       u_char  offset_max;
+       u_char  nr_divisor;
+       u_char  lp_probe_bit;
+       u_int   features;
+#define FE_LED0                (1<<0)
+#define FE_WIDE                (1<<1)    /* Wide data transfers */
+#define FE_ULTRA       (1<<2)    /* Ultra speed 20Mtrans/sec */
+#define FE_ULTRA2      (1<<3)    /* Ultra 2 - 40 Mtrans/sec */
+#define FE_DBLR                (1<<4)    /* Clock doubler present */
+#define FE_QUAD                (1<<5)    /* Clock quadrupler present */
+#define FE_ERL         (1<<6)    /* Enable read line */
+#define FE_CLSE                (1<<7)    /* Cache line size enable */
+#define FE_WRIE                (1<<8)    /* Write & Invalidate enable */
+#define FE_ERMP                (1<<9)    /* Enable read multiple */
+#define FE_BOF         (1<<10)   /* Burst opcode fetch */
+#define FE_DFS         (1<<11)   /* DMA fifo size */
+#define FE_PFEN                (1<<12)   /* Prefetch enable */
+#define FE_LDSTR       (1<<13)   /* Load/Store supported */
+#define FE_RAM         (1<<14)   /* On chip RAM present */
+#define FE_VARCLK      (1<<15)   /* Clock frequency may vary */
+#define FE_RAM8K       (1<<16)   /* On chip RAM sized 8Kb */
+#define FE_64BIT       (1<<17)   /* 64-bit PCI BUS interface */
+#define FE_IO256       (1<<18)   /* Requires full 256 bytes in PCI space */
+#define FE_NOPM                (1<<19)   /* Scripts handles phase mismatch */
+#define FE_LEDC                (1<<20)   /* Hardware control of LED */
+#define FE_ULTRA3      (1<<21)   /* Ultra 3 - 80 Mtrans/sec DT */
+#define FE_66MHZ       (1<<22)   /* 66MHz PCI support */
+#define FE_CRC         (1<<23)   /* CRC support */
+#define FE_DIFF                (1<<24)   /* SCSI HVD support */
+#define FE_DFBC                (1<<25)   /* Have DFBC register */
+#define FE_LCKFRQ      (1<<26)   /* Have LCKFRQ */
+#define FE_C10         (1<<27)   /* Various C10 core (mis)features */
+#define FE_U3EN                (1<<28)   /* U3EN bit usable */
+#define FE_DAC         (1<<29)   /* Support PCI DAC (64 bit addressing) */
+#define FE_ISTAT1      (1<<30)   /* Have ISTAT1, MBOX0, MBOX1 registers */
+
+#define FE_CACHE_SET   (FE_ERL|FE_CLSE|FE_WRIE|FE_ERMP)
+#define FE_CACHE0_SET  (FE_CACHE_SET & ~FE_ERL)
+};
+
+/*
+ *     Symbios NVRAM data format
+ */
+#define SYMBIOS_NVRAM_SIZE 368
+#define SYMBIOS_NVRAM_ADDRESS 0x100
+
+struct Symbios_nvram {
+/* Header 6 bytes */
+       u_short type;           /* 0x0000 */
+       u_short byte_count;     /* excluding header/trailer */
+       u_short checksum;
+
+/* Controller set up 20 bytes */
+       u_char  v_major;        /* 0x00 */
+       u_char  v_minor;        /* 0x30 */
+       u32     boot_crc;
+       u_short flags;
+#define SYMBIOS_SCAM_ENABLE    (1)
+#define SYMBIOS_PARITY_ENABLE  (1<<1)
+#define SYMBIOS_VERBOSE_MSGS   (1<<2)
+#define SYMBIOS_CHS_MAPPING    (1<<3)
+#define SYMBIOS_NO_NVRAM       (1<<3)  /* ??? */
+       u_short flags1;
+#define SYMBIOS_SCAN_HI_LO     (1)
+       u_short term_state;
+#define SYMBIOS_TERM_CANT_PROGRAM      (0)
+#define SYMBIOS_TERM_ENABLED           (1)
+#define SYMBIOS_TERM_DISABLED          (2)
+       u_short rmvbl_flags;
+#define SYMBIOS_RMVBL_NO_SUPPORT       (0)
+#define SYMBIOS_RMVBL_BOOT_DEVICE      (1)
+#define SYMBIOS_RMVBL_MEDIA_INSTALLED  (2)
+       u_char  host_id;
+       u_char  num_hba;        /* 0x04 */
+       u_char  num_devices;    /* 0x10 */
+       u_char  max_scam_devices;       /* 0x04 */
+       u_char  num_valid_scam_devices; /* 0x00 */
+       u_char  flags2;
+#define SYMBIOS_AVOID_BUS_RESET                (1<<2)
+
+/* Boot order 14 bytes * 4 */
+       struct Symbios_host{
+               u_short type;           /* 4:8xx / 0:nok */
+               u_short device_id;      /* PCI device id */
+               u_short vendor_id;      /* PCI vendor id */
+               u_char  bus_nr;         /* PCI bus number */
+               u_char  device_fn;      /* PCI device/function number << 3*/
+               u_short word8;
+               u_short flags;
+#define        SYMBIOS_INIT_SCAN_AT_BOOT       (1)
+               u_short io_port;        /* PCI io_port address */
+       } host[4];
+
+/* Targets 8 bytes * 16 */
+       struct Symbios_target {
+               u_char  flags;
+#define SYMBIOS_DISCONNECT_ENABLE      (1)
+#define SYMBIOS_SCAN_AT_BOOT_TIME      (1<<1)
+#define SYMBIOS_SCAN_LUNS              (1<<2)
+#define SYMBIOS_QUEUE_TAGS_ENABLED     (1<<3)
+               u_char  rsvd;
+               u_char  bus_width;      /* 0x08/0x10 */
+               u_char  sync_offset;
+               u_short sync_period;    /* 4*period factor */
+               u_short timeout;
+       } target[16];
+/* Scam table 8 bytes * 4 */
+       struct Symbios_scam {
+               u_short id;
+               u_short method;
+#define SYMBIOS_SCAM_DEFAULT_METHOD    (0)
+#define SYMBIOS_SCAM_DONT_ASSIGN       (1)
+#define SYMBIOS_SCAM_SET_SPECIFIC_ID   (2)
+#define SYMBIOS_SCAM_USE_ORDER_GIVEN   (3)
+               u_short status;
+#define SYMBIOS_SCAM_UNKNOWN           (0)
+#define SYMBIOS_SCAM_DEVICE_NOT_FOUND  (1)
+#define SYMBIOS_SCAM_ID_NOT_SET                (2)
+#define SYMBIOS_SCAM_ID_VALID          (3)
+               u_char  target_id;
+               u_char  rsvd;
+       } scam[4];
+
+       u_char  spare_devices[15*8];
+       u_char  trailer[6];             /* 0xfe 0xfe 0x00 0x00 0x00 0x00 */
+};
+typedef struct Symbios_nvram   Symbios_nvram;
+typedef struct Symbios_host    Symbios_host;
+typedef struct Symbios_target  Symbios_target;
+typedef struct Symbios_scam    Symbios_scam;
+
+/*
+ *     Tekram NvRAM data format.
+ */
+#define TEKRAM_NVRAM_SIZE 64
+#define TEKRAM_93C46_NVRAM_ADDRESS 0
+#define TEKRAM_24C16_NVRAM_ADDRESS 0x40
+
+struct Tekram_nvram {
+       struct Tekram_target {
+               u_char  flags;
+#define        TEKRAM_PARITY_CHECK             (1)
+#define TEKRAM_SYNC_NEGO               (1<<1)
+#define TEKRAM_DISCONNECT_ENABLE       (1<<2)
+#define        TEKRAM_START_CMD                (1<<3)
+#define TEKRAM_TAGGED_COMMANDS         (1<<4)
+#define TEKRAM_WIDE_NEGO               (1<<5)
+               u_char  sync_index;
+               u_short word2;
+       } target[16];
+       u_char  host_id;
+       u_char  flags;
+#define TEKRAM_MORE_THAN_2_DRIVES      (1)
+#define TEKRAM_DRIVES_SUP_1GB          (1<<1)
+#define        TEKRAM_RESET_ON_POWER_ON        (1<<2)
+#define TEKRAM_ACTIVE_NEGATION         (1<<3)
+#define TEKRAM_IMMEDIATE_SEEK          (1<<4)
+#define        TEKRAM_SCAN_LUNS                (1<<5)
+#define        TEKRAM_REMOVABLE_FLAGS          (3<<6)  /* 0: disable; */
+                                               /* 1: boot device; 2:all */
+       u_char  boot_delay_index;
+       u_char  max_tags_index;
+       u_short flags1;
+#define TEKRAM_F2_F6_ENABLED           (1)
+       u_short spare[29];
+};
+typedef struct Tekram_nvram    Tekram_nvram;
+typedef struct Tekram_target   Tekram_target;
+
+/*
+ *     SYM53C8XX IO register data structure.
+ */
+struct sym_reg {
+/*00*/  u8     nc_scntl0;      /* full arb., ena parity, par->ATN  */
+
+/*01*/  u8     nc_scntl1;      /* no reset                         */
+        #define   ISCON   0x10  /* connected to scsi               */
+        #define   CRST    0x08  /* force reset                      */
+        #define   IARB    0x02  /* immediate arbitration            */
+
+/*02*/  u8     nc_scntl2;      /* no disconnect expected           */
+       #define   SDU     0x80  /* cmd: disconnect will raise error */
+       #define   CHM     0x40  /* sta: chained mode                */
+       #define   WSS     0x08  /* sta: wide scsi send           [W]*/
+       #define   WSR     0x01  /* sta: wide scsi received       [W]*/
+
+/*03*/  u8     nc_scntl3;      /* cnf system clock dependent       */
+       #define   EWS     0x08  /* cmd: enable wide scsi         [W]*/
+       #define   ULTRA   0x80  /* cmd: ULTRA enable                */
+                               /* bits 0-2, 7 rsvd for C1010       */
+
+/*04*/  u8     nc_scid;        /* cnf host adapter scsi address    */
+       #define   RRE     0x40  /* r/w:e enable response to resel.  */
+       #define   SRE     0x20  /* r/w:e enable response to select  */
+
+/*05*/  u8     nc_sxfer;       /* ### Sync speed and count         */
+                               /* bits 6-7 rsvd for C1010          */
+
+/*06*/  u8     nc_sdid;        /* ### Destination-ID               */
+
+/*07*/  u8     nc_gpreg;       /* ??? IO-Pins                      */
+
+/*08*/  u8     nc_sfbr;        /* ### First byte received          */
+
+/*09*/  u8     nc_socl;
+       #define   CREQ    0x80  /* r/w: SCSI-REQ                    */
+       #define   CACK    0x40  /* r/w: SCSI-ACK                    */
+       #define   CBSY    0x20  /* r/w: SCSI-BSY                    */
+       #define   CSEL    0x10  /* r/w: SCSI-SEL                    */
+       #define   CATN    0x08  /* r/w: SCSI-ATN                    */
+       #define   CMSG    0x04  /* r/w: SCSI-MSG                    */
+       #define   CC_D    0x02  /* r/w: SCSI-C_D                    */
+       #define   CI_O    0x01  /* r/w: SCSI-I_O                    */
+
+/*0a*/  u8     nc_ssid;
+
+/*0b*/  u8     nc_sbcl;
+
+/*0c*/  u8     nc_dstat;
+        #define   DFE     0x80  /* sta: dma fifo empty              */
+        #define   MDPE    0x40  /* int: master data parity error    */
+        #define   BF      0x20  /* int: script: bus fault           */
+        #define   ABRT    0x10  /* int: script: command aborted     */
+        #define   SSI     0x08  /* int: script: single step         */
+        #define   SIR     0x04  /* int: script: interrupt instruct. */
+        #define   IID     0x01  /* int: script: illegal instruct.   */
+
+/*0d*/  u8     nc_sstat0;
+        #define   ILF     0x80  /* sta: data in SIDL register lsb   */
+        #define   ORF     0x40  /* sta: data in SODR register lsb   */
+        #define   OLF     0x20  /* sta: data in SODL register lsb   */
+        #define   AIP     0x10  /* sta: arbitration in progress     */
+        #define   LOA     0x08  /* sta: arbitration lost            */
+        #define   WOA     0x04  /* sta: arbitration won             */
+        #define   IRST    0x02  /* sta: scsi reset signal           */
+        #define   SDP     0x01  /* sta: scsi parity signal          */
+
+/*0e*/  u8     nc_sstat1;
+       #define   FF3210  0xf0  /* sta: bytes in the scsi fifo      */
+
+/*0f*/  u8     nc_sstat2;
+        #define   ILF1    0x80  /* sta: data in SIDL register msb[W]*/
+        #define   ORF1    0x40  /* sta: data in SODR register msb[W]*/
+        #define   OLF1    0x20  /* sta: data in SODL register msb[W]*/
+        #define   DM      0x04  /* sta: DIFFSENS mismatch (895/6 only) */
+        #define   LDSC    0x02  /* sta: disconnect & reconnect      */
+
+/*10*/  u8     nc_dsa;         /* --> Base page                    */
+/*11*/  u8     nc_dsa1;
+/*12*/  u8     nc_dsa2;
+/*13*/  u8     nc_dsa3;
+
+/*14*/  u8     nc_istat;       /* --> Main Command and status      */
+        #define   CABRT   0x80  /* cmd: abort current operation     */
+        #define   SRST    0x40  /* mod: reset chip                  */
+        #define   SIGP    0x20  /* r/w: message from host to script */
+        #define   SEM     0x10  /* r/w: message between host + script  */
+        #define   CON     0x08  /* sta: connected to scsi           */
+        #define   INTF    0x04  /* sta: int on the fly (reset by wr)*/
+        #define   SIP     0x02  /* sta: scsi-interrupt              */
+        #define   DIP     0x01  /* sta: host/script interrupt       */
+
+/*15*/  u8     nc_istat1;      /* 896 only */
+        #define   FLSH    0x04  /* sta: chip is flushing            */
+        #define   SCRUN   0x02  /* sta: scripts are running         */
+        #define   SIRQD   0x01  /* r/w: disable INT pin             */
+
+/*16*/  u8     nc_mbox0;       /* 896 only */
+/*17*/  u8     nc_mbox1;       /* 896 only */
+
+/*18*/ u8      nc_ctest0;
+/*19*/  u8     nc_ctest1;
+
+/*1a*/  u8     nc_ctest2;
+       #define   CSIGP   0x40
+                               /* bits 0-2,7 rsvd for C1010        */
+
+/*1b*/  u8     nc_ctest3;
+       #define   FLF     0x08  /* cmd: flush dma fifo              */
+       #define   CLF     0x04  /* cmd: clear dma fifo              */
+       #define   FM      0x02  /* mod: fetch pin mode              */
+       #define   WRIE    0x01  /* mod: write and invalidate enable */
+                               /* bits 4-7 rsvd for C1010          */
+
+/*1c*/  u32    nc_temp;        /* ### Temporary stack              */
+
+/*20*/ u8      nc_dfifo;
+/*21*/  u8     nc_ctest4;
+       #define   BDIS    0x80  /* mod: burst disable               */
+       #define   MPEE    0x08  /* mod: master parity error enable  */
+
+/*22*/  u8     nc_ctest5;
+       #define   DFS     0x20  /* mod: dma fifo size               */
+                               /* bits 0-1, 3-7 rsvd for C1010     */
+
+/*23*/  u8     nc_ctest6;
+
+/*24*/  u32    nc_dbc;         /* ### Byte count and command       */
+/*28*/  u32    nc_dnad;        /* ### Next command register        */
+/*2c*/  u32    nc_dsp;         /* --> Script Pointer               */
+/*30*/  u32    nc_dsps;        /* --> Script pointer save/opcode#2 */
+
+/*34*/  u8     nc_scratcha;    /* Temporary register a            */
+/*35*/  u8     nc_scratcha1;
+/*36*/  u8     nc_scratcha2;
+/*37*/  u8     nc_scratcha3;
+
+/*38*/  u8     nc_dmode;
+       #define   BL_2    0x80  /* mod: burst length shift value +2 */
+       #define   BL_1    0x40  /* mod: burst length shift value +1 */
+       #define   ERL     0x08  /* mod: enable read line            */
+       #define   ERMP    0x04  /* mod: enable read multiple        */
+       #define   BOF     0x02  /* mod: burst op code fetch         */
+
+/*39*/  u8     nc_dien;
+/*3a*/  u8     nc_sbr;
+
+/*3b*/  u8     nc_dcntl;       /* --> Script execution control     */
+       #define   CLSE    0x80  /* mod: cache line size enable      */
+       #define   PFF     0x40  /* cmd: pre-fetch flush             */
+       #define   PFEN    0x20  /* mod: pre-fetch enable            */
+       #define   SSM     0x10  /* mod: single step mode            */
+       #define   IRQM    0x08  /* mod: irq mode (1 = totem pole !) */
+       #define   STD     0x04  /* cmd: start dma mode              */
+       #define   IRQD    0x02  /* mod: irq disable                 */
+       #define   NOCOM   0x01  /* cmd: protect sfbr while reselect */
+                               /* bits 0-1 rsvd for C1010          */
+
+/*3c*/  u32    nc_adder;
+
+/*40*/  u16    nc_sien;        /* -->: interrupt enable            */
+/*42*/  u16    nc_sist;        /* <--: interrupt status            */
+        #define   SBMC    0x1000/* sta: SCSI Bus Mode Change (895/6 only) */
+        #define   STO     0x0400/* sta: timeout (select)            */
+        #define   GEN     0x0200/* sta: timeout (general)           */
+        #define   HTH     0x0100/* sta: timeout (handshake)         */
+        #define   MA      0x80  /* sta: phase mismatch              */
+        #define   CMP     0x40  /* sta: arbitration complete        */
+        #define   SEL     0x20  /* sta: selected by another device  */
+        #define   RSL     0x10  /* sta: reselected by another device*/
+        #define   SGE     0x08  /* sta: gross error (over/underflow)*/
+        #define   UDC     0x04  /* sta: unexpected disconnect       */
+        #define   RST     0x02  /* sta: scsi bus reset detected     */
+        #define   PAR     0x01  /* sta: scsi parity error           */
+
+/*44*/  u8     nc_slpar;
+/*45*/  u8     nc_swide;
+/*46*/  u8     nc_macntl;
+/*47*/  u8     nc_gpcntl;
+/*48*/  u8     nc_stime0;      /* cmd: timeout for select&handshake*/
+/*49*/  u8     nc_stime1;      /* cmd: timeout user defined        */
+/*4a*/  u16    nc_respid;      /* sta: Reselect-IDs                */
+
+/*4c*/  u8     nc_stest0;
+
+/*4d*/  u8     nc_stest1;
+       #define   SCLK    0x80  /* Use the PCI clock as SCSI clock      */
+       #define   DBLEN   0x08  /* clock doubler running                */
+       #define   DBLSEL  0x04  /* clock doubler selected               */
+  
+
+/*4e*/  u8     nc_stest2;
+       #define   ROF     0x40  /* reset scsi offset (after gross error!) */
+       #define   EXT     0x02  /* extended filtering                     */
+
+/*4f*/  u8     nc_stest3;
+       #define   TE     0x80   /* c: tolerAnt enable */
+       #define   HSC    0x20   /* c: Halt SCSI Clock */
+       #define   CSF    0x02   /* c: clear scsi fifo */
+
+/*50*/  u16    nc_sidl;        /* Lowlevel: latched from scsi data */
+/*52*/  u8     nc_stest4;
+       #define   SMODE  0xc0   /* SCSI bus mode      (895/6 only) */
+       #define    SMODE_HVD 0x40       /* High Voltage Differential       */
+       #define    SMODE_SE  0x80       /* Single Ended                    */
+       #define    SMODE_LVD 0xc0       /* Low Voltage Differential        */
+       #define   LCKFRQ 0x20   /* Frequency Lock (895/6 only)     */
+                               /* bits 0-5 rsvd for C1010         */
+
+/*53*/  u8     nc_53_;
+/*54*/  u16    nc_sodl;        /* Lowlevel: data out to scsi data  */
+/*56*/ u8      nc_ccntl0;      /* Chip Control 0 (896)             */
+       #define   ENPMJ  0x80   /* Enable Phase Mismatch Jump       */
+       #define   PMJCTL 0x40   /* Phase Mismatch Jump Control      */
+       #define   ENNDJ  0x20   /* Enable Non Data PM Jump          */
+       #define   DISFC  0x10   /* Disable Auto FIFO Clear          */
+       #define   DILS   0x02   /* Disable Internal Load/Store      */
+       #define   DPR    0x01   /* Disable Pipe Req                 */
+
+/*57*/ u8      nc_ccntl1;      /* Chip Control 1 (896)             */
+       #define   ZMOD   0x80   /* High Impedance Mode              */
+       #define   DDAC   0x08   /* Disable Dual Address Cycle       */
+       #define   XTIMOD 0x04   /* 64-bit Table Ind. Indexing Mode  */
+       #define   EXTIBMV 0x02  /* Enable 64-bit Table Ind. BMOV    */
+       #define   EXDBMV 0x01   /* Enable 64-bit Direct BMOV        */
+
+/*58*/  u16    nc_sbdl;        /* Lowlevel: data from scsi data    */
+/*5a*/  u16    nc_5a_;
+
+/*5c*/  u8     nc_scr0;        /* Working register B               */
+/*5d*/  u8     nc_scr1;
+/*5e*/  u8     nc_scr2;
+/*5f*/  u8     nc_scr3;
+
+/*60*/  u8     nc_scrx[64];    /* Working register C-R             */
+/*a0*/ u32     nc_mmrs;        /* Memory Move Read Selector        */
+/*a4*/ u32     nc_mmws;        /* Memory Move Write Selector       */
+/*a8*/ u32     nc_sfs;         /* Script Fetch Selector            */
+/*ac*/ u32     nc_drs;         /* DSA Relative Selector            */
+/*b0*/ u32     nc_sbms;        /* Static Block Move Selector       */
+/*b4*/ u32     nc_dbms;        /* Dynamic Block Move Selector      */
+/*b8*/ u32     nc_dnad64;      /* DMA Next Address 64              */
+/*bc*/ u16     nc_scntl4;      /* C1010 only                       */
+       #define   U3EN    0x80  /* Enable Ultra 3                   */
+       #define   AIPCKEN 0x40  /* AIP checking enable              */
+                               /* Also enable AIP generation on C10-33*/
+       #define   XCLKH_DT 0x08 /* Extra clock of data hold on DT edge */
+       #define   XCLKH_ST 0x04 /* Extra clock of data hold on ST edge */
+       #define   XCLKS_DT 0x02 /* Extra clock of data set  on DT edge */
+       #define   XCLKS_ST 0x01 /* Extra clock of data set  on ST edge */
+/*be*/ u8      nc_aipcntl0;    /* AIP Control 0 C1010 only         */
+/*bf*/ u8      nc_aipcntl1;    /* AIP Control 1 C1010 only         */
+       #define DISAIP  0x08    /* Disable AIP generation C10-66 only  */
+/*c0*/ u32     nc_pmjad1;      /* Phase Mismatch Jump Address 1    */
+/*c4*/ u32     nc_pmjad2;      /* Phase Mismatch Jump Address 2    */
+/*c8*/ u8      nc_rbc;         /* Remaining Byte Count             */
+/*c9*/ u8      nc_rbc1;
+/*ca*/ u8      nc_rbc2;
+/*cb*/ u8      nc_rbc3;
+
+/*cc*/ u8      nc_ua;          /* Updated Address                  */
+/*cd*/ u8      nc_ua1;
+/*ce*/ u8      nc_ua2;
+/*cf*/ u8      nc_ua3;
+/*d0*/ u32     nc_esa;         /* Entry Storage Address            */
+/*d4*/ u8      nc_ia;          /* Instruction Address              */
+/*d5*/ u8      nc_ia1;
+/*d6*/ u8      nc_ia2;
+/*d7*/ u8      nc_ia3;
+/*d8*/ u32     nc_sbc;         /* SCSI Byte Count (3 bytes only)   */
+/*dc*/ u32     nc_csbc;        /* Cumulative SCSI Byte Count       */
+                                /* Following for C1010 only         */
+/*e0*/ u16    nc_crcpad;       /* CRC Value                        */
+/*e2*/ u8     nc_crccntl0;     /* CRC control register             */
+       #define   SNDCRC  0x10  /* Send CRC Request                 */
+/*e3*/ u8     nc_crccntl1;     /* CRC control register             */
+/*e4*/ u32    nc_crcdata;      /* CRC data register                */
+/*e8*/ u32    nc_e8_;
+/*ec*/ u32    nc_ec_;
+/*f0*/ u16    nc_dfbc;         /* DMA FIFO byte count              */ 
+};
+
+/*-----------------------------------------------------------
+ *
+ *     Utility macros for the script.
+ *
+ *-----------------------------------------------------------
+ */
+
+#define REGJ(p,r) (offsetof(struct sym_reg, p ## r))
+#define REG(r) REGJ (nc_, r)
+
+/*-----------------------------------------------------------
+ *
+ *     SCSI phases
+ *
+ *-----------------------------------------------------------
+ */
+
+#define        SCR_DATA_OUT    0x00000000
+#define        SCR_DATA_IN     0x01000000
+#define        SCR_COMMAND     0x02000000
+#define        SCR_STATUS      0x03000000
+#define        SCR_DT_DATA_OUT 0x04000000
+#define        SCR_DT_DATA_IN  0x05000000
+#define SCR_MSG_OUT    0x06000000
+#define SCR_MSG_IN      0x07000000
+/* DT phases are illegal for non Ultra3 mode */
+#define SCR_ILG_OUT    0x04000000
+#define SCR_ILG_IN     0x05000000
+
+/*-----------------------------------------------------------
+ *
+ *     Data transfer via SCSI.
+ *
+ *-----------------------------------------------------------
+ *
+ *     MOVE_ABS (LEN)
+ *     <<start address>>
+ *
+ *     MOVE_IND (LEN)
+ *     <<dnad_offset>>
+ *
+ *     MOVE_TBL
+ *     <<dnad_offset>>
+ *
+ *-----------------------------------------------------------
+ */
+
+#define OPC_MOVE          0x08000000
+
+#define SCR_MOVE_ABS(l) ((0x00000000 | OPC_MOVE) | (l))
+/* #define SCR_MOVE_IND(l) ((0x20000000 | OPC_MOVE) | (l)) */
+#define SCR_MOVE_TBL     (0x10000000 | OPC_MOVE)
+
+#define SCR_CHMOV_ABS(l) ((0x00000000) | (l))
+/* #define SCR_CHMOV_IND(l) ((0x20000000) | (l)) */
+#define SCR_CHMOV_TBL     (0x10000000)
+
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+/* We steal the `indirect addressing' flag for target mode MOVE in scripts */
+
+#define OPC_TCHMOVE        0x08000000
+
+#define SCR_TCHMOVE_ABS(l) ((0x20000000 | OPC_TCHMOVE) | (l))
+#define SCR_TCHMOVE_TBL     (0x30000000 | OPC_TCHMOVE)
+
+#define SCR_TMOV_ABS(l)    ((0x20000000) | (l))
+#define SCR_TMOV_TBL        (0x30000000)
+#endif
+
+struct sym_tblmove {
+        u32  size;
+        u32  addr;
+};
+
+/*-----------------------------------------------------------
+ *
+ *     Selection
+ *
+ *-----------------------------------------------------------
+ *
+ *     SEL_ABS | SCR_ID (0..15)    [ | REL_JMP]
+ *     <<alternate_address>>
+ *
+ *     SEL_TBL | << dnad_offset>>  [ | REL_JMP]
+ *     <<alternate_address>>
+ *
+ *-----------------------------------------------------------
+ */
+
+#define        SCR_SEL_ABS     0x40000000
+#define        SCR_SEL_ABS_ATN 0x41000000
+#define        SCR_SEL_TBL     0x42000000
+#define        SCR_SEL_TBL_ATN 0x43000000
+
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+#define        SCR_RESEL_ABS     0x40000000
+#define        SCR_RESEL_ABS_ATN 0x41000000
+#define        SCR_RESEL_TBL     0x42000000
+#define        SCR_RESEL_TBL_ATN 0x43000000
+#endif
+
+struct sym_tblsel {
+        u_char  sel_scntl4;    /* C1010 only */
+        u_char  sel_sxfer;
+        u_char  sel_id;
+        u_char  sel_scntl3;
+};
+
+#define SCR_JMP_REL     0x04000000
+#define SCR_ID(id)     (((u32)(id)) << 16)
+
+/*-----------------------------------------------------------
+ *
+ *     Waiting for Disconnect or Reselect
+ *
+ *-----------------------------------------------------------
+ *
+ *     WAIT_DISC
+ *     dummy: <<alternate_address>>
+ *
+ *     WAIT_RESEL
+ *     <<alternate_address>>
+ *
+ *-----------------------------------------------------------
+ */
+
+#define        SCR_WAIT_DISC   0x48000000
+#define SCR_WAIT_RESEL  0x50000000
+
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+#define        SCR_DISCONNECT  0x48000000
+#endif
+
+/*-----------------------------------------------------------
+ *
+ *     Bit Set / Reset
+ *
+ *-----------------------------------------------------------
+ *
+ *     SET (flags {|.. })
+ *
+ *     CLR (flags {|.. })
+ *
+ *-----------------------------------------------------------
+ */
+
+#define SCR_SET(f)     (0x58000000 | (f))
+#define SCR_CLR(f)     (0x60000000 | (f))
+
+#define        SCR_CARRY       0x00000400
+#define        SCR_TRG         0x00000200
+#define        SCR_ACK         0x00000040
+#define        SCR_ATN         0x00000008
+
+
+/*-----------------------------------------------------------
+ *
+ *     Memory to memory move
+ *
+ *-----------------------------------------------------------
+ *
+ *     COPY (bytecount)
+ *     << source_address >>
+ *     << destination_address >>
+ *
+ *     SCR_COPY   sets the NO FLUSH option by default.
+ *     SCR_COPY_F does not set this option.
+ *
+ *     For chips which do not support this option,
+ *     sym_fw_bind_script() will remove this bit.
+ *
+ *-----------------------------------------------------------
+ */
+
+#define SCR_NO_FLUSH 0x01000000
+
+#define SCR_COPY(n) (0xc0000000 | SCR_NO_FLUSH | (n))
+#define SCR_COPY_F(n) (0xc0000000 | (n))
+
+/*-----------------------------------------------------------
+ *
+ *     Register move and binary operations
+ *
+ *-----------------------------------------------------------
+ *
+ *     SFBR_REG (reg, op, data)        reg  = SFBR op data
+ *     << 0 >>
+ *
+ *     REG_SFBR (reg, op, data)        SFBR = reg op data
+ *     << 0 >>
+ *
+ *     REG_REG  (reg, op, data)        reg  = reg op data
+ *     << 0 >>
+ *
+ *-----------------------------------------------------------
+ *
+ *     On 825A, 875, 895 and 896 chips the content 
+ *     of SFBR register can be used as data (SCR_SFBR_DATA).
+ *     The 896 has additionnal IO registers starting at 
+ *     offset 0x80. Bit 7 of register offset is stored in 
+ *     bit 7 of the SCRIPTS instruction first DWORD.
+ *
+ *-----------------------------------------------------------
+ */
+
+#define SCR_REG_OFS(ofs) ((((ofs) & 0x7f) << 16ul) + ((ofs) & 0x80)) 
+
+#define SCR_SFBR_REG(reg,op,data) \
+        (0x68000000 | (SCR_REG_OFS(REG(reg))) | (op) | (((data)&0xff)<<8ul))
+
+#define SCR_REG_SFBR(reg,op,data) \
+        (0x70000000 | (SCR_REG_OFS(REG(reg))) | (op) | (((data)&0xff)<<8ul))
+
+#define SCR_REG_REG(reg,op,data) \
+        (0x78000000 | (SCR_REG_OFS(REG(reg))) | (op) | (((data)&0xff)<<8ul))
+
+
+#define      SCR_LOAD   0x00000000
+#define      SCR_SHL    0x01000000
+#define      SCR_OR     0x02000000
+#define      SCR_XOR    0x03000000
+#define      SCR_AND    0x04000000
+#define      SCR_SHR    0x05000000
+#define      SCR_ADD    0x06000000
+#define      SCR_ADDC   0x07000000
+
+#define      SCR_SFBR_DATA   (0x00800000>>8ul) /* Use SFBR as data */
+
+/*-----------------------------------------------------------
+ *
+ *     FROM_REG (reg)            SFBR = reg
+ *     << 0 >>
+ *
+ *     TO_REG   (reg)            reg  = SFBR
+ *     << 0 >>
+ *
+ *     LOAD_REG (reg, data)      reg  = <data>
+ *     << 0 >>
+ *
+ *     LOAD_SFBR(data)           SFBR = <data>
+ *     << 0 >>
+ *
+ *-----------------------------------------------------------
+ */
+
+#define        SCR_FROM_REG(reg) \
+       SCR_REG_SFBR(reg,SCR_OR,0)
+
+#define        SCR_TO_REG(reg) \
+       SCR_SFBR_REG(reg,SCR_OR,0)
+
+#define        SCR_LOAD_REG(reg,data) \
+       SCR_REG_REG(reg,SCR_LOAD,data)
+
+#define SCR_LOAD_SFBR(data) \
+        (SCR_REG_SFBR (gpreg, SCR_LOAD, data))
+
+/*-----------------------------------------------------------
+ *
+ *     LOAD  from memory   to register.
+ *     STORE from register to memory.
+ *
+ *     Only supported by 810A, 860, 825A, 875, 895 and 896.
+ *
+ *-----------------------------------------------------------
+ *
+ *     LOAD_ABS (LEN)
+ *     <<start address>>
+ *
+ *     LOAD_REL (LEN)        (DSA relative)
+ *     <<dsa_offset>>
+ *
+ *-----------------------------------------------------------
+ */
+
+#define SCR_REG_OFS2(ofs) (((ofs) & 0xff) << 16ul)
+#define SCR_NO_FLUSH2  0x02000000
+#define SCR_DSA_REL2   0x10000000
+
+#define SCR_LOAD_R(reg, how, n) \
+        (0xe1000000 | how | (SCR_REG_OFS2(REG(reg))) | (n))
+
+#define SCR_STORE_R(reg, how, n) \
+        (0xe0000000 | how | (SCR_REG_OFS2(REG(reg))) | (n))
+
+#define SCR_LOAD_ABS(reg, n)   SCR_LOAD_R(reg, SCR_NO_FLUSH2, n)
+#define SCR_LOAD_REL(reg, n)   SCR_LOAD_R(reg, SCR_NO_FLUSH2|SCR_DSA_REL2, n)
+#define SCR_LOAD_ABS_F(reg, n) SCR_LOAD_R(reg, 0, n)
+#define SCR_LOAD_REL_F(reg, n) SCR_LOAD_R(reg, SCR_DSA_REL2, n)
+
+#define SCR_STORE_ABS(reg, n)  SCR_STORE_R(reg, SCR_NO_FLUSH2, n)
+#define SCR_STORE_REL(reg, n)  SCR_STORE_R(reg, SCR_NO_FLUSH2|SCR_DSA_REL2,n)
+#define SCR_STORE_ABS_F(reg, n)        SCR_STORE_R(reg, 0, n)
+#define SCR_STORE_REL_F(reg, n)        SCR_STORE_R(reg, SCR_DSA_REL2, n)
+
+
+/*-----------------------------------------------------------
+ *
+ *     Waiting for Disconnect or Reselect
+ *
+ *-----------------------------------------------------------
+ *
+ *     JUMP            [ | IFTRUE/IFFALSE ( ... ) ]
+ *     <<address>>
+ *
+ *     JUMPR           [ | IFTRUE/IFFALSE ( ... ) ]
+ *     <<distance>>
+ *
+ *     CALL            [ | IFTRUE/IFFALSE ( ... ) ]
+ *     <<address>>
+ *
+ *     CALLR           [ | IFTRUE/IFFALSE ( ... ) ]
+ *     <<distance>>
+ *
+ *     RETURN          [ | IFTRUE/IFFALSE ( ... ) ]
+ *     <<dummy>>
+ *
+ *     INT             [ | IFTRUE/IFFALSE ( ... ) ]
+ *     <<ident>>
+ *
+ *     INT_FLY         [ | IFTRUE/IFFALSE ( ... ) ]
+ *     <<ident>>
+ *
+ *     Conditions:
+ *          WHEN (phase)
+ *          IF   (phase)
+ *          CARRYSET
+ *          DATA (data, mask)
+ *
+ *-----------------------------------------------------------
+ */
+
+#define SCR_NO_OP       0x80000000
+#define SCR_JUMP        0x80080000
+#define SCR_JUMP64      0x80480000
+#define SCR_JUMPR       0x80880000
+#define SCR_CALL        0x88080000
+#define SCR_CALLR       0x88880000
+#define SCR_RETURN      0x90080000
+#define SCR_INT         0x98080000
+#define SCR_INT_FLY     0x98180000
+
+#define IFFALSE(arg)   (0x00080000 | (arg))
+#define IFTRUE(arg)    (0x00000000 | (arg))
+
+#define WHEN(phase)    (0x00030000 | (phase))
+#define IF(phase)      (0x00020000 | (phase))
+
+#define DATA(D)        (0x00040000 | ((D) & 0xff))
+#define MASK(D,M)      (0x00040000 | (((M ^ 0xff) & 0xff) << 8ul)|((D) & 0xff))
+
+#define CARRYSET       (0x00200000)
+
+/*-----------------------------------------------------------
+ *
+ *     SCSI  constants.
+ *
+ *-----------------------------------------------------------
+ */
+
+/*
+ *     Messages
+ */
+
+#define        M_COMPLETE      (0x00)
+#define        M_EXTENDED      (0x01)
+#define        M_SAVE_DP       (0x02)
+#define        M_RESTORE_DP    (0x03)
+#define        M_DISCONNECT    (0x04)
+#define        M_ID_ERROR      (0x05)
+#define        M_ABORT         (0x06)
+#define        M_REJECT        (0x07)
+#define        M_NOOP          (0x08)
+#define        M_PARITY        (0x09)
+#define        M_LCOMPLETE     (0x0a)
+#define        M_FCOMPLETE     (0x0b)
+#define        M_RESET         (0x0c)
+#define        M_ABORT_TAG     (0x0d)
+#define        M_CLEAR_QUEUE   (0x0e)
+#define        M_INIT_REC      (0x0f)
+#define        M_REL_REC       (0x10)
+#define        M_TERMINATE     (0x11)
+#define        M_SIMPLE_TAG    (0x20)
+#define        M_HEAD_TAG      (0x21)
+#define        M_ORDERED_TAG   (0x22)
+#define        M_IGN_RESIDUE   (0x23)
+#define        M_IDENTIFY      (0x80)
+
+#define        M_X_MODIFY_DP   (0x00)
+#define        M_X_SYNC_REQ    (0x01)
+#define        M_X_WIDE_REQ    (0x03)
+#define        M_X_PPR_REQ     (0x04)
+
+/*
+ *     PPR protocol options
+ */
+#define        PPR_OPT_IU      (0x01)
+#define        PPR_OPT_DT      (0x02)
+#define        PPR_OPT_QAS     (0x04)
+#define PPR_OPT_MASK   (0x07)
+
+/*
+ *     Status
+ */
+
+#define        S_GOOD          (0x00)
+#define        S_CHECK_COND    (0x02)
+#define        S_COND_MET      (0x04)
+#define        S_BUSY          (0x08)
+#define        S_INT           (0x10)
+#define        S_INT_COND_MET  (0x14)
+#define        S_CONFLICT      (0x18)
+#define        S_TERMINATED    (0x20)
+#define        S_QUEUE_FULL    (0x28)
+#define        S_ILLEGAL       (0xff)
+
+#endif /* defined SYM_DEFS_H */
diff --git a/xen/drivers/scsi/sym53c8xx_2/sym_fw.c b/xen/drivers/scsi/sym53c8xx_2/sym_fw.c
new file mode 100644 (file)
index 0000000..46858e8
--- /dev/null
@@ -0,0 +1,617 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000  Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ *         Wolfgang Stanglmeier        <wolf@cologne.de>
+ *         Stefan Esser                <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994  Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef __FreeBSD__
+#include <dev/sym/sym_glue.h>
+#else
+#include "sym_glue.h"
+#endif
+
+/*
+ *  Macros used for all firmwares.
+ */
+#define        SYM_GEN_A(s, label)     ((short) offsetof(s, label)),
+#define        SYM_GEN_B(s, label)     ((short) offsetof(s, label)),
+#define        SYM_GEN_Z(s, label)     ((short) offsetof(s, label)),
+#define        PADDR_A(label)          SYM_GEN_PADDR_A(struct SYM_FWA_SCR, label)
+#define        PADDR_B(label)          SYM_GEN_PADDR_B(struct SYM_FWB_SCR, label)
+
+
+#if    SYM_CONF_GENERIC_SUPPORT
+/*
+ *  Allocate firmware #1 script area.
+ */
+#define        SYM_FWA_SCR             sym_fw1a_scr
+#define        SYM_FWB_SCR             sym_fw1b_scr
+#define        SYM_FWZ_SCR             sym_fw1z_scr
+#ifdef __FreeBSD__
+#include <dev/sym/sym_fw1.h>
+#else
+#include "sym_fw1.h"
+#endif
+static struct sym_fwa_ofs sym_fw1a_ofs = {
+       SYM_GEN_FW_A(struct SYM_FWA_SCR)
+};
+static struct sym_fwb_ofs sym_fw1b_ofs = {
+       SYM_GEN_FW_B(struct SYM_FWB_SCR)
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+       SYM_GEN_B(struct SYM_FWB_SCR, data_io)
+#endif
+};
+static struct sym_fwz_ofs sym_fw1z_ofs = {
+       SYM_GEN_FW_Z(struct SYM_FWZ_SCR)
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+       SYM_GEN_Z(struct SYM_FWZ_SCR, start_ram)
+#endif
+};
+#undef SYM_FWA_SCR
+#undef SYM_FWB_SCR
+#undef SYM_FWZ_SCR
+#endif /* SYM_CONF_GENERIC_SUPPORT */
+
+/*
+ *  Allocate firmware #2 script area.
+ */
+#define        SYM_FWA_SCR             sym_fw2a_scr
+#define        SYM_FWB_SCR             sym_fw2b_scr
+#define        SYM_FWZ_SCR             sym_fw2z_scr
+#ifdef __FreeBSD__
+#include <dev/sym/sym_fw2.h>
+#else
+#include "sym_fw2.h"
+#endif
+static struct sym_fwa_ofs sym_fw2a_ofs = {
+       SYM_GEN_FW_A(struct SYM_FWA_SCR)
+};
+static struct sym_fwb_ofs sym_fw2b_ofs = {
+       SYM_GEN_FW_B(struct SYM_FWB_SCR)
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+       SYM_GEN_B(struct SYM_FWB_SCR, data_io)
+#endif
+       SYM_GEN_B(struct SYM_FWB_SCR, start64)
+       SYM_GEN_B(struct SYM_FWB_SCR, pm_handle)
+};
+static struct sym_fwz_ofs sym_fw2z_ofs = {
+       SYM_GEN_FW_Z(struct SYM_FWZ_SCR)
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+       SYM_GEN_Z(struct SYM_FWZ_SCR, start_ram)
+       SYM_GEN_Z(struct SYM_FWZ_SCR, start_ram64)
+#endif
+};
+#undef SYM_FWA_SCR
+#undef SYM_FWB_SCR
+#undef SYM_FWZ_SCR
+
+#undef SYM_GEN_A
+#undef SYM_GEN_B
+#undef SYM_GEN_Z
+#undef PADDR_A
+#undef PADDR_B
+
+#if    SYM_CONF_GENERIC_SUPPORT
+/*
+ *  Patch routine for firmware #1.
+ */
+static void
+sym_fw1_patch(hcb_p np)
+{
+       struct sym_fw1a_scr *scripta0;
+       struct sym_fw1b_scr *scriptb0;
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+       struct sym_fw1z_scr *scriptz0 = 
+               (struct sym_fw1z_scr *) np->scriptz0;
+#endif
+
+       scripta0 = (struct sym_fw1a_scr *) np->scripta0;
+       scriptb0 = (struct sym_fw1b_scr *) np->scriptb0;
+
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+       /*
+        *  Set up BUS physical address of SCRIPTS that is to 
+        *  be copied to on-chip RAM by the SCRIPTS processor.
+        */
+       scriptz0->scripta0_ba[0]        = cpu_to_scr(vtobus(scripta0));
+#endif
+
+       /*
+        *  Remove LED support if not needed.
+        */
+       if (!(np->features & FE_LED0)) {
+               scripta0->idle[0]       = cpu_to_scr(SCR_NO_OP);
+               scripta0->reselected[0] = cpu_to_scr(SCR_NO_OP);
+               scripta0->start[0]      = cpu_to_scr(SCR_NO_OP);
+       }
+
+#ifdef SYM_CONF_IARB_SUPPORT
+       /*
+        *    If user does not want to use IMMEDIATE ARBITRATION
+        *    when we are reselected while attempting to arbitrate,
+        *    patch the SCRIPTS accordingly with a SCRIPT NO_OP.
+        */
+       if (!SYM_CONF_SET_IARB_ON_ARB_LOST)
+               scripta0->ungetjob[0] = cpu_to_scr(SCR_NO_OP);
+#endif
+       /*
+        *  Patch some data in SCRIPTS.
+        *  - start and done queue initial bus address.
+        *  - target bus address table bus address.
+        */
+       scriptb0->startpos[0]   = cpu_to_scr(np->squeue_ba);
+       scriptb0->done_pos[0]   = cpu_to_scr(np->dqueue_ba);
+       scriptb0->targtbl[0]    = cpu_to_scr(np->targtbl_ba);
+}
+#endif /* SYM_CONF_GENERIC_SUPPORT */
+
+/*
+ *  Patch routine for firmware #2.
+ */
+static void
+sym_fw2_patch(hcb_p np)
+{
+       struct sym_fw2a_scr *scripta0;
+       struct sym_fw2b_scr *scriptb0;
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+       struct sym_fw2z_scr *scriptz0 = 
+               (struct sym_fw2z_scr *) np->scriptz0;
+#endif
+
+       scripta0 = (struct sym_fw2a_scr *) np->scripta0;
+       scriptb0 = (struct sym_fw2b_scr *) np->scriptb0;
+
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+       /*
+        *  Set up BUS physical address of SCRIPTS that is to 
+        *  be copied to on-chip RAM by the SCRIPTS processor.
+        */
+       scriptz0->scripta0_ba64[0]      = /* Nothing is missing here */
+       scriptz0->scripta0_ba[0]        = cpu_to_scr(vtobus(scripta0));
+       scriptz0->scriptb0_ba64[0]      = cpu_to_scr(vtobus(scriptb0));
+       scriptz0->ram_seg64[0]          = np->scr_ram_seg;
+#endif
+
+       /*
+        *  Remove LED support if not needed.
+        */
+       if (!(np->features & FE_LED0)) {
+               scripta0->idle[0]       = cpu_to_scr(SCR_NO_OP);
+               scripta0->reselected[0] = cpu_to_scr(SCR_NO_OP);
+               scripta0->start[0]      = cpu_to_scr(SCR_NO_OP);
+       }
+
+#if   SYM_CONF_DMA_ADDRESSING_MODE == 2
+       /*
+        *  Remove useless 64 bit DMA specific SCRIPTS, 
+        *  when this feature is not available.
+        */
+       if (!np->use_dac) {
+               scripta0->is_dmap_dirty[0] = cpu_to_scr(SCR_NO_OP);
+               scripta0->is_dmap_dirty[1] = 0;
+               scripta0->is_dmap_dirty[2] = cpu_to_scr(SCR_NO_OP);
+               scripta0->is_dmap_dirty[3] = 0;
+       }
+#endif
+
+#ifdef SYM_CONF_IARB_SUPPORT
+       /*
+        *    If user does not want to use IMMEDIATE ARBITRATION
+        *    when we are reselected while attempting to arbitrate,
+        *    patch the SCRIPTS accordingly with a SCRIPT NO_OP.
+        */
+       if (!SYM_CONF_SET_IARB_ON_ARB_LOST)
+               scripta0->ungetjob[0] = cpu_to_scr(SCR_NO_OP);
+#endif
+       /*
+        *  Patch some variable in SCRIPTS.
+        *  - start and done queue initial bus address.
+        *  - target bus address table bus address.
+        */
+       scriptb0->startpos[0]   = cpu_to_scr(np->squeue_ba);
+       scriptb0->done_pos[0]   = cpu_to_scr(np->dqueue_ba);
+       scriptb0->targtbl[0]    = cpu_to_scr(np->targtbl_ba);
+
+       /*
+        *  Remove the load of SCNTL4 on reselection if not a C10.
+        */
+       if (!(np->features & FE_C10)) {
+               scripta0->resel_scntl4[0] = cpu_to_scr(SCR_NO_OP);
+               scripta0->resel_scntl4[1] = cpu_to_scr(0);
+       }
+
+       /*
+        *  Remove a couple of work-arounds specific to C1010 if 
+        *  they are not desirable. See `sym_fw2.h' for more details.
+        */
+       if (!(np->device_id == PCI_ID_LSI53C1010_2 &&
+             np->revision_id < 0x1 &&
+             np->pciclk_khz < 60000)) {
+               scripta0->datao_phase[0] = cpu_to_scr(SCR_NO_OP);
+               scripta0->datao_phase[1] = cpu_to_scr(0);
+       }
+       if (!(np->device_id == PCI_ID_LSI53C1010 &&
+             /* np->revision_id < 0xff */ 1)) {
+               scripta0->sel_done[0] = cpu_to_scr(SCR_NO_OP);
+               scripta0->sel_done[1] = cpu_to_scr(0);
+       }
+
+       /*
+        *  Patch some other variables in SCRIPTS.
+        *  These ones are loaded by the SCRIPTS processor.
+        */
+       scriptb0->pm0_data_addr[0] =
+               cpu_to_scr(np->scripta_ba + 
+                          offsetof(struct sym_fw2a_scr, pm0_data));
+       scriptb0->pm1_data_addr[0] =
+               cpu_to_scr(np->scripta_ba + 
+                          offsetof(struct sym_fw2a_scr, pm1_data));
+}
+
+/*
+ *  Fill the data area in scripts.
+ *  To be done for all firmwares.
+ */
+static void
+sym_fw_fill_data (u32 *in, u32 *out)
+{
+       int     i;
+
+       for (i = 0; i < SYM_CONF_MAX_SG; i++) {
+               *in++  = SCR_CHMOV_TBL ^ SCR_DATA_IN;
+               *in++  = offsetof (struct sym_dsb, data[i]);
+               *out++ = SCR_CHMOV_TBL ^ SCR_DATA_OUT;
+               *out++ = offsetof (struct sym_dsb, data[i]);
+       }
+}
+
+/*
+ *  Setup useful script bus addresses.
+ *  To be done for all firmwares.
+ */
+static void 
+sym_fw_setup_bus_addresses(hcb_p np, struct sym_fw *fw)
+{
+       u32 *pa;
+       u_short *po;
+       int i;
+
+       /*
+        *  Build the bus address table for script A 
+        *  from the script A offset table.
+        */
+       po = (u_short *) fw->a_ofs;
+       pa = (u32 *) &np->fwa_bas;
+       for (i = 0 ; i < sizeof(np->fwa_bas)/sizeof(u32) ; i++)
+               pa[i] = np->scripta_ba + po[i];
+
+       /*
+        *  Same for script B.
+        */
+       po = (u_short *) fw->b_ofs;
+       pa = (u32 *) &np->fwb_bas;
+       for (i = 0 ; i < sizeof(np->fwb_bas)/sizeof(u32) ; i++)
+               pa[i] = np->scriptb_ba + po[i];
+
+       /*
+        *  Same for script Z.
+        */
+       po = (u_short *) fw->z_ofs;
+       pa = (u32 *) &np->fwz_bas;
+       for (i = 0 ; i < sizeof(np->fwz_bas)/sizeof(u32) ; i++)
+               pa[i] = np->scriptz_ba + po[i];
+}
+
+#if    SYM_CONF_GENERIC_SUPPORT
+/*
+ *  Setup routine for firmware #1.
+ */
+static void 
+sym_fw1_setup(hcb_p np, struct sym_fw *fw)
+{
+       struct sym_fw1a_scr *scripta0;
+       struct sym_fw1b_scr *scriptb0;
+
+       scripta0 = (struct sym_fw1a_scr *) np->scripta0;
+       scriptb0 = (struct sym_fw1b_scr *) np->scriptb0;
+
+       /*
+        *  Fill variable parts in scripts.
+        */
+       sym_fw_fill_data(scripta0->data_in, scripta0->data_out);
+
+       /*
+        *  Setup bus addresses used from the C code..
+        */
+       sym_fw_setup_bus_addresses(np, fw);
+}
+#endif /* SYM_CONF_GENERIC_SUPPORT */
+
+/*
+ *  Setup routine for firmware #2.
+ */
+static void 
+sym_fw2_setup(hcb_p np, struct sym_fw *fw)
+{
+       struct sym_fw2a_scr *scripta0;
+       struct sym_fw2b_scr *scriptb0;
+
+       scripta0 = (struct sym_fw2a_scr *) np->scripta0;
+       scriptb0 = (struct sym_fw2b_scr *) np->scriptb0;
+
+       /*
+        *  Fill variable parts in scripts.
+        */
+       sym_fw_fill_data(scripta0->data_in, scripta0->data_out);
+
+       /*
+        *  Setup bus addresses used from the C code..
+        */
+       sym_fw_setup_bus_addresses(np, fw);
+}
+
+/*
+ *  Allocate firmware descriptors.
+ */
+#if    SYM_CONF_GENERIC_SUPPORT
+static struct sym_fw sym_fw1 = SYM_FW_ENTRY(sym_fw1, "NCR-generic");
+#endif /* SYM_CONF_GENERIC_SUPPORT */
+static struct sym_fw sym_fw2 = SYM_FW_ENTRY(sym_fw2, "LOAD/STORE-based");
+
+/*
+ *  Find the most appropriate firmware for a chip.
+ */
+struct sym_fw * 
+sym_find_firmware(struct sym_pci_chip *chip)
+{
+       if (chip->features & FE_LDSTR)
+               return &sym_fw2;
+#if    SYM_CONF_GENERIC_SUPPORT
+       else if (!(chip->features & (FE_PFEN|FE_NOPM|FE_DAC)))
+               return &sym_fw1;
+#endif
+       else
+               return 0;
+}
+
+/*
+ *  Bind a script to physical addresses.
+ */
+void sym_fw_bind_script (hcb_p np, u32 *start, int len)
+{
+       u32 opcode, new, old, tmp1, tmp2;
+       u32 *end, *cur;
+       int relocs;
+
+       cur = start;
+       end = start + len/4;
+
+       while (cur < end) {
+
+               opcode = *cur;
+
+               /*
+                *  If we forget to change the length
+                *  in scripts, a field will be
+                *  padded with 0. This is an illegal
+                *  command.
+                */
+               if (opcode == 0) {
+                       printf ("%s: ERROR0 IN SCRIPT at %d.\n",
+                               sym_name(np), (int) (cur-start));
+                       MDELAY (10000);
+                       ++cur;
+                       continue;
+               };
+
+               /*
+                *  We use the bogus value 0xf00ff00f ;-)
+                *  to reserve data area in SCRIPTS.
+                */
+               if (opcode == SCR_DATA_ZERO) {
+                       *cur++ = 0;
+                       continue;
+               }
+
+               if (DEBUG_FLAGS & DEBUG_SCRIPT)
+                       printf ("%d:  <%x>\n", (int) (cur-start),
+                               (unsigned)opcode);
+
+               /*
+                *  We don't have to decode ALL commands
+                */
+               switch (opcode >> 28) {
+               case 0xf:
+                       /*
+                        *  LOAD / STORE DSA relative, don't relocate.
+                        */
+                       relocs = 0;
+                       break;
+               case 0xe:
+                       /*
+                        *  LOAD / STORE absolute.
+                        */
+                       relocs = 1;
+                       break;
+               case 0xc:
+                       /*
+                        *  COPY has TWO arguments.
+                        */
+                       relocs = 2;
+                       tmp1 = cur[1];
+                       tmp2 = cur[2];
+                       if ((tmp1 ^ tmp2) & 3) {
+                               printf ("%s: ERROR1 IN SCRIPT at %d.\n",
+                                       sym_name(np), (int) (cur-start));
+                               MDELAY (10000);
+                       }
+                       /*
+                        *  If PREFETCH feature not enabled, remove 
+                        *  the NO FLUSH bit if present.
+                        */
+                       if ((opcode & SCR_NO_FLUSH) &&
+                           !(np->features & FE_PFEN)) {
+                               opcode = (opcode & ~SCR_NO_FLUSH);
+                       }
+                       break;
+               case 0x0:
+                       /*
+                        *  MOVE/CHMOV (absolute address)
+                        */
+                       if (!(np->features & FE_WIDE))
+                               opcode = (opcode | OPC_MOVE);
+                       relocs = 1;
+                       break;
+               case 0x1:
+                       /*
+                        *  MOVE/CHMOV (table indirect)
+                        */
+                       if (!(np->features & FE_WIDE))
+                               opcode = (opcode | OPC_MOVE);
+                       relocs = 0;
+                       break;
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+               case 0x2:
+                       /*
+                        *  MOVE/CHMOV in target role (absolute address)
+                        */
+                       opcode &= ~0x20000000;
+                       if (!(np->features & FE_WIDE))
+                               opcode = (opcode & ~OPC_TCHMOVE);
+                       relocs = 1;
+                       break;
+               case 0x3:
+                       /*
+                        *  MOVE/CHMOV in target role (table indirect)
+                        */
+                       opcode &= ~0x20000000;
+                       if (!(np->features & FE_WIDE))
+                               opcode = (opcode & ~OPC_TCHMOVE);
+                       relocs = 0;
+                       break;
+#endif
+               case 0x8:
+                       /*
+                        *  JUMP / CALL
+                        *  dont't relocate if relative :-)
+                        */
+                       if (opcode & 0x00800000)
+                               relocs = 0;
+                       else if ((opcode & 0xf8400000) == 0x80400000)/*JUMP64*/
+                               relocs = 2;
+                       else
+                               relocs = 1;
+                       break;
+               case 0x4:
+               case 0x5:
+               case 0x6:
+               case 0x7:
+                       relocs = 1;
+                       break;
+               default:
+                       relocs = 0;
+                       break;
+               };
+
+               /*
+                *  Scriptify:) the opcode.
+                */
+               *cur++ = cpu_to_scr(opcode);
+
+               /*
+                *  If no relocation, assume 1 argument 
+                *  and just scriptize:) it.
+                */
+               if (!relocs) {
+                       *cur = cpu_to_scr(*cur);
+                       ++cur;
+                       continue;
+               }
+
+               /*
+                *  Otherwise performs all needed relocations.
+                */
+               while (relocs--) {
+                       old = *cur;
+
+                       switch (old & RELOC_MASK) {
+                       case RELOC_REGISTER:
+                               new = (old & ~RELOC_MASK) + np->mmio_ba;
+                               break;
+                       case RELOC_LABEL_A:
+                               new = (old & ~RELOC_MASK) + np->scripta_ba;
+                               break;
+                       case RELOC_LABEL_B:
+                               new = (old & ~RELOC_MASK) + np->scriptb_ba;
+                               break;
+                       case RELOC_SOFTC:
+                               new = (old & ~RELOC_MASK) + np->hcb_ba;
+                               break;
+                       case 0:
+                               /*
+                                *  Don't relocate a 0 address.
+                                *  They are mostly used for patched or 
+                                *  script self-modified areas.
+                                */
+                               if (old == 0) {
+                                       new = old;
+                                       break;
+                               }
+                               /* fall through */
+                       default:
+                               new = 0;
+                               panic("sym_fw_bind_script: "
+                                     "weird relocation %x\n", old);
+                               break;
+                       }
+
+                       *cur++ = cpu_to_scr(new);
+               }
+       };
+}
diff --git a/xen/drivers/scsi/sym53c8xx_2/sym_fw.h b/xen/drivers/scsi/sym53c8xx_2/sym_fw.h
new file mode 100644 (file)
index 0000000..993b08c
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000  Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ *         Wolfgang Stanglmeier        <wolf@cologne.de>
+ *         Stefan Esser                <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994  Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef        SYM_FW_H
+#define        SYM_FW_H
+/*
+ *  Macro used to generate interfaces for script A.
+ */
+#define SYM_GEN_FW_A(s)                                                        \
+       SYM_GEN_A(s, start)             SYM_GEN_A(s, getjob_begin)      \
+       SYM_GEN_A(s, getjob_end)                                        \
+       SYM_GEN_A(s, select)            SYM_GEN_A(s, wf_sel_done)       \
+       SYM_GEN_A(s, send_ident)                                        \
+       SYM_GEN_A(s, dispatch)          SYM_GEN_A(s, init)              \
+       SYM_GEN_A(s, clrack)            SYM_GEN_A(s, complete_error)    \
+       SYM_GEN_A(s, done)              SYM_GEN_A(s, done_end)          \
+       SYM_GEN_A(s, idle)              SYM_GEN_A(s, ungetjob)          \
+       SYM_GEN_A(s, reselect)                                          \
+       SYM_GEN_A(s, resel_tag)         SYM_GEN_A(s, resel_dsa)         \
+       SYM_GEN_A(s, resel_no_tag)                                      \
+       SYM_GEN_A(s, data_in)           SYM_GEN_A(s, data_in2)          \
+       SYM_GEN_A(s, data_out)          SYM_GEN_A(s, data_out2)         \
+       SYM_GEN_A(s, pm0_data)          SYM_GEN_A(s, pm1_data)
+
+/*
+ *  Macro used to generate interfaces for script B.
+ */
+#define SYM_GEN_FW_B(s)                                                        \
+       SYM_GEN_B(s, no_data)                                           \
+       SYM_GEN_B(s, sel_for_abort)     SYM_GEN_B(s, sel_for_abort_1)   \
+       SYM_GEN_B(s, msg_bad)           SYM_GEN_B(s, msg_weird)         \
+       SYM_GEN_B(s, wdtr_resp)         SYM_GEN_B(s, send_wdtr)         \
+       SYM_GEN_B(s, sdtr_resp)         SYM_GEN_B(s, send_sdtr)         \
+       SYM_GEN_B(s, ppr_resp)          SYM_GEN_B(s, send_ppr)          \
+       SYM_GEN_B(s, nego_bad_phase)                                    \
+       SYM_GEN_B(s, ident_break)       SYM_GEN_B(s, ident_break_atn)   \
+       SYM_GEN_B(s, sdata_in)          SYM_GEN_B(s, resel_bad_lun)     \
+       SYM_GEN_B(s, bad_i_t_l)         SYM_GEN_B(s, bad_i_t_l_q)       \
+       SYM_GEN_B(s, wsr_ma_helper)
+
+/*
+ *  Macro used to generate interfaces for script Z.
+ */
+#define SYM_GEN_FW_Z(s)                                                        \
+       SYM_GEN_Z(s, snooptest)         SYM_GEN_Z(s, snoopend)
+
+/*
+ *  Generates structure interface that contains 
+ *  offsets within script A, B and Z.
+ */
+#define        SYM_GEN_A(s, label)     s label;
+#define        SYM_GEN_B(s, label)     s label;
+#define        SYM_GEN_Z(s, label)     s label;
+struct sym_fwa_ofs {
+       SYM_GEN_FW_A(u_short)
+};
+struct sym_fwb_ofs {
+       SYM_GEN_FW_B(u_short)
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+       SYM_GEN_B(u_short, data_io)
+#endif
+       SYM_GEN_B(u_short, start64)
+       SYM_GEN_B(u_short, pm_handle)
+};
+struct sym_fwz_ofs {
+       SYM_GEN_FW_Z(u_short)
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+       SYM_GEN_Z(u_short, start_ram)
+       SYM_GEN_Z(u_short, start_ram64)
+#endif
+};
+
+/*
+ *  Generates structure interface that contains 
+ *  bus addresses within script A, B and Z.
+ */
+struct sym_fwa_ba {
+       SYM_GEN_FW_A(u32)
+};
+struct sym_fwb_ba {
+       SYM_GEN_FW_B(u32)
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+       SYM_GEN_B(u32, data_io)
+#endif
+       SYM_GEN_B(u32, start64);
+       SYM_GEN_B(u32, pm_handle);
+};
+struct sym_fwz_ba {
+       SYM_GEN_FW_Z(u32)
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+       SYM_GEN_Z(u32, start_ram)
+       SYM_GEN_Z(u32, start_ram64)
+#endif
+};
+#undef SYM_GEN_A
+#undef SYM_GEN_B
+#undef SYM_GEN_Z
+
+/*
+ *  Let cc know about the name of the controller data structure.
+ *  We need this for function prototype declarations just below.
+ */
+struct sym_hcb;
+
+/*
+ *  Generic structure that defines a firmware.
+ */ 
+struct sym_fw {
+       char    *name;          /* Name we want to print out    */
+       u32     *a_base;        /* Pointer to script A template */
+       int     a_size;         /* Size of script A             */
+       struct  sym_fwa_ofs
+               *a_ofs;         /* Useful offsets in script A   */
+       u32     *b_base;        /* Pointer to script B template */
+       int     b_size;         /* Size of script B             */
+       struct  sym_fwb_ofs
+               *b_ofs;         /* Useful offsets in script B   */
+       u32     *z_base;        /* Pointer to script Z template */
+       int     z_size;         /* Size of script Z             */
+       struct  sym_fwz_ofs
+               *z_ofs;         /* Useful offsets in script Z   */
+       /* Setup and patch methods for this firmware */
+       void    (*setup)(struct sym_hcb *, struct sym_fw *);
+       void    (*patch)(struct sym_hcb *);
+};
+
+/*
+ *  Macro used to declare a firmware.
+ */
+#define SYM_FW_ENTRY(fw, name)                                 \
+{                                                              \
+       name,                                                   \
+       (u32 *) &fw##a_scr, sizeof(fw##a_scr), &fw##a_ofs,      \
+       (u32 *) &fw##b_scr, sizeof(fw##b_scr), &fw##b_ofs,      \
+       (u32 *) &fw##z_scr, sizeof(fw##z_scr), &fw##z_ofs,      \
+       fw##_setup, fw##_patch                                  \
+}
+
+/*
+ *  Macros used from the C code to get useful
+ *  SCRIPTS bus addresses.
+ */
+#define SCRIPTA_BA(np, label)  (np->fwa_bas.label)
+#define SCRIPTB_BA(np, label)  (np->fwb_bas.label)
+#define SCRIPTZ_BA(np, label)  (np->fwz_bas.label)
+
+/*
+ *  Macros used by scripts definitions.
+ *
+ *  HADDR_1 generates a reference to a field of the controller data.
+ *  HADDR_2 generates a reference to a field of the controller data
+ *          with offset.
+ *  RADDR_1 generates a reference to a script processor register.
+ *  RADDR_2 generates a reference to a script processor register
+ *          with offset.
+ *  PADDR_A generates a reference to another part of script A.
+ *  PADDR_B generates a reference to another part of script B.
+ *
+ *  SYM_GEN_PADDR_A and SYM_GEN_PADDR_B are used to define respectively 
+ *  the PADDR_A and PADDR_B macros for each firmware by setting argument 
+ *  `s' to the name of the corresponding structure.
+ *
+ *  SCR_DATA_ZERO is used to allocate a DWORD of data in scripts areas.
+ */
+
+#define        RELOC_SOFTC     0x40000000
+#define        RELOC_LABEL_A   0x50000000
+#define        RELOC_REGISTER  0x60000000
+#define        RELOC_LABEL_B   0x80000000
+#define        RELOC_MASK      0xf0000000
+
+#define        HADDR_1(label)     (RELOC_SOFTC    | offsetof(struct sym_hcb, label))
+#define        HADDR_2(label,ofs) (RELOC_SOFTC    | \
+                               (offsetof(struct sym_hcb, label)+(ofs)))
+#define        RADDR_1(label)     (RELOC_REGISTER | REG(label))
+#define        RADDR_2(label,ofs) (RELOC_REGISTER | ((REG(label))+(ofs)))
+
+#define SYM_GEN_PADDR_A(s, label) (RELOC_LABEL_A  | offsetof(s, label))
+#define SYM_GEN_PADDR_B(s, label) (RELOC_LABEL_B  | offsetof(s, label))
+
+#define SCR_DATA_ZERO  0xf00ff00f
+
+#endif /* SYM_FW_H */
diff --git a/xen/drivers/scsi/sym53c8xx_2/sym_fw1.h b/xen/drivers/scsi/sym53c8xx_2/sym_fw1.h
new file mode 100644 (file)
index 0000000..a859a05
--- /dev/null
@@ -0,0 +1,1874 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000  Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ *         Wolfgang Stanglmeier        <wolf@cologne.de>
+ *         Stefan Esser                <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994  Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ *  Scripts for SYMBIOS-Processor
+ *
+ *  We have to know the offsets of all labels before we reach 
+ *  them (for forward jumps). Therefore we declare a struct 
+ *  here. If you make changes inside the script,
+ *
+ *  DONT FORGET TO CHANGE THE LENGTHS HERE!
+ */
+
+/*
+ *  Script fragments which are loaded into the on-chip RAM 
+ *  of 825A, 875, 876, 895, 895A, 896 and 1010 chips.
+ *  Must not exceed 4K bytes.
+ */
+struct SYM_FWA_SCR {
+       u32 start               [ 11];
+       u32 getjob_begin        [  4];
+       u32 _sms_a10            [  5];
+       u32 getjob_end          [  4];
+       u32 _sms_a20            [  4];
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+       u32 select              [  8];
+#else
+       u32 select              [  6];
+#endif
+       u32 _sms_a30            [  5];
+       u32 wf_sel_done         [  2];
+       u32 send_ident          [  2];
+#ifdef SYM_CONF_IARB_SUPPORT
+       u32 select2             [  8];
+#else
+       u32 select2             [  2];
+#endif
+       u32 command             [  2];
+       u32 dispatch            [ 28];
+       u32 sel_no_cmd          [ 10];
+       u32 init                [  6];
+       u32 clrack              [  4];
+       u32 datai_done          [ 11];
+       u32 datai_done_wsr      [ 20];
+       u32 datao_done          [ 11];
+       u32 datao_done_wss      [  6];
+       u32 datai_phase         [  5];
+       u32 datao_phase         [  5];
+       u32 msg_in              [  2];
+       u32 msg_in2             [ 10];
+#ifdef SYM_CONF_IARB_SUPPORT
+       u32 status              [ 14];
+#else
+       u32 status              [ 10];
+#endif
+       u32 complete            [  6];
+       u32 complete2           [  8];
+       u32 _sms_a40            [ 12];
+       u32 done                [  5];
+       u32 _sms_a50            [  5];
+       u32 _sms_a60            [  2];
+       u32 done_end            [  4];
+       u32 complete_error      [  5];
+       u32 save_dp             [ 11];
+       u32 restore_dp          [  7];
+       u32 disconnect          [ 11];
+       u32 disconnect2         [  5];
+       u32 _sms_a65            [  3];
+#ifdef SYM_CONF_IARB_SUPPORT
+       u32 idle                [  4];
+#else
+       u32 idle                [  2];
+#endif
+#ifdef SYM_CONF_IARB_SUPPORT
+       u32 ungetjob            [  7];
+#else
+       u32 ungetjob            [  5];
+#endif
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+       u32 reselect            [  4];
+#else
+       u32 reselect            [  2];
+#endif
+       u32 reselected          [ 19];
+       u32 _sms_a70            [  6];
+       u32 _sms_a80            [  4];
+       u32 reselected1         [ 25];
+       u32 _sms_a90            [  4];
+       u32 resel_lun0          [  7];
+       u32 _sms_a100           [  4];
+       u32 resel_tag           [  8];
+#if   SYM_CONF_MAX_TASK*4 > 512
+       u32 _sms_a110           [ 23];
+#elif SYM_CONF_MAX_TASK*4 > 256
+       u32 _sms_a110           [ 17];
+#else
+       u32 _sms_a110           [ 13];
+#endif
+       u32 _sms_a120           [  2];
+       u32 resel_go            [  4];
+       u32 _sms_a130           [  7];
+       u32 resel_dsa           [  2];
+       u32 resel_dsa1          [  4];
+       u32 _sms_a140           [  7];
+       u32 resel_no_tag        [  4];
+       u32 _sms_a145           [  7];
+       u32 data_in             [SYM_CONF_MAX_SG * 2];
+       u32 data_in2            [  4];
+       u32 data_out            [SYM_CONF_MAX_SG * 2];
+       u32 data_out2           [  4];
+       u32 pm0_data            [ 12];
+       u32 pm0_data_out        [  6];
+       u32 pm0_data_end        [  7];
+       u32 pm_data_end         [  4];
+       u32 _sms_a150           [  4];
+       u32 pm1_data            [ 12];
+       u32 pm1_data_out        [  6];
+       u32 pm1_data_end        [  9];
+};
+
+/*
+ *  Script fragments which stay in main memory for all chips 
+ *  except for chips that support 8K on-chip RAM.
+ */
+struct SYM_FWB_SCR {
+       u32 no_data             [  2];
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+       u32 sel_for_abort       [ 18];
+#else
+       u32 sel_for_abort       [ 16];
+#endif
+       u32 sel_for_abort_1     [  2];
+       u32 msg_in_etc          [ 12];
+       u32 msg_received        [  5];
+       u32 msg_weird_seen      [  5];
+       u32 msg_extended        [ 17];
+       u32 _sms_b10            [  4];
+       u32 msg_bad             [  6];
+       u32 msg_weird           [  4];
+       u32 msg_weird1          [  8];
+       u32 wdtr_resp           [  6];
+       u32 send_wdtr           [  4];
+       u32 sdtr_resp           [  6];
+       u32 send_sdtr           [  4];
+       u32 ppr_resp            [  6];
+       u32 send_ppr            [  4];
+       u32 nego_bad_phase      [  4];
+       u32 msg_out             [  4];
+       u32 msg_out_done        [  4];
+       u32 data_ovrun          [  3];
+       u32 data_ovrun1         [ 22];
+       u32 data_ovrun2         [  8];
+       u32 abort_resel         [ 16];
+       u32 resend_ident        [  4];
+       u32 ident_break         [  4];
+       u32 ident_break_atn     [  4];
+       u32 sdata_in            [  6];
+       u32 resel_bad_lun       [  4];
+       u32 bad_i_t_l           [  4];
+       u32 bad_i_t_l_q         [  4];
+       u32 bad_status          [  7];
+       u32 wsr_ma_helper       [  4];
+
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+       /* Unknown direction handling */
+       u32 data_io             [  2];
+       u32 data_io_com         [  8];
+       u32 data_io_out         [  7];
+#endif
+       /* Data area */
+       u32 zero                [  1];
+       u32 scratch             [  1];
+       u32 scratch1            [  1];
+       u32 prev_done           [  1];
+       u32 done_pos            [  1];
+       u32 nextjob             [  1];
+       u32 startpos            [  1];
+       u32 targtbl             [  1];
+};
+
+/*
+ *  Script fragments used at initialisations.
+ *  Only runs out of main memory.
+ */
+struct SYM_FWZ_SCR {
+       u32 snooptest           [  9];
+       u32 snoopend            [  2];
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+       u32 start_ram           [  1];
+       u32 scripta0_ba         [  4];
+#endif
+};
+
+static struct SYM_FWA_SCR SYM_FWA_SCR = {
+/*--------------------------< START >----------------------------*/ {
+       /*
+        *  Switch the LED on.
+        *  Will be patched with a NO_OP if LED
+        *  not needed or not desired.
+        */
+       SCR_REG_REG (gpreg, SCR_AND, 0xfe),
+               0,
+       /*
+        *      Clear SIGP.
+        */
+       SCR_FROM_REG (ctest2),
+               0,
+       /*
+        *  Stop here if the C code wants to perform 
+        *  some error recovery procedure manually.
+        *  (Indicate this by setting SEM in ISTAT)
+        */
+       SCR_FROM_REG (istat),
+               0,
+       /*
+        *  Report to the C code the next position in 
+        *  the start queue the SCRIPTS will schedule.
+        *  The C code must not change SCRATCHA.
+        */
+       SCR_COPY (4),
+               PADDR_B (startpos),
+               RADDR_1 (scratcha),
+       SCR_INT ^ IFTRUE (MASK (SEM, SEM)),
+               SIR_SCRIPT_STOPPED,
+       /*
+        *  Start the next job.
+        *
+        *  @DSA     = start point for this job.
+        *  SCRATCHA = address of this job in the start queue.
+        *
+        *  We will restore startpos with SCRATCHA if we fails the 
+        *  arbitration or if it is the idle job.
+        *
+        *  The below GETJOB_BEGIN to GETJOB_END section of SCRIPTS 
+        *  is a critical path. If it is partially executed, it then 
+        *  may happen that the job address is not yet in the DSA 
+        *  and the next queue position points to the next JOB.
+        */
+}/*-------------------------< GETJOB_BEGIN >---------------------*/,{
+       /*
+        *  Copy to a fixed location both the next STARTPOS 
+        *  and the current JOB address, using self modifying 
+        *  SCRIPTS.
+        */
+       SCR_COPY (4),
+               RADDR_1 (scratcha),
+               PADDR_A (_sms_a10),
+       SCR_COPY (8),
+}/*-------------------------< _SMS_A10 >-------------------------*/,{
+               0,
+               PADDR_B (nextjob),
+       /*
+        *  Move the start address to TEMP using self-
+        *  modifying SCRIPTS and jump indirectly to 
+        *  that address.
+        */
+       SCR_COPY (4),
+               PADDR_B (nextjob),
+               RADDR_1 (dsa),
+}/*-------------------------< GETJOB_END >-----------------------*/,{
+       SCR_COPY (4),
+               RADDR_1 (dsa),
+               PADDR_A (_sms_a20),
+       SCR_COPY (4),
+}/*-------------------------< _SMS_A20 >-------------------------*/,{
+               0,
+               RADDR_1 (temp),
+       SCR_RETURN,
+               0,
+}/*-------------------------< SELECT >---------------------------*/,{
+       /*
+        *  DSA contains the address of a scheduled
+        *      data structure.
+        *
+        *  SCRATCHA contains the address of the start queue  
+        *      entry which points to the next job.
+        *
+        *  Set Initiator mode.
+        *
+        *  (Target mode is left as an exercise for the reader)
+        */
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+       SCR_CLR (SCR_TRG),
+               0,
+#endif
+       /*
+        *      And try to select this target.
+        */
+       SCR_SEL_TBL_ATN ^ offsetof (struct sym_dsb, select),
+               PADDR_A (ungetjob),
+       /*
+        *  Now there are 4 possibilities:
+        *
+        *  (1) The chip looses arbitration.
+        *  This is ok, because it will try again,
+        *  when the bus becomes idle.
+        *  (But beware of the timeout function!)
+        *
+        *  (2) The chip is reselected.
+        *  Then the script processor takes the jump
+        *  to the RESELECT label.
+        *
+        *  (3) The chip wins arbitration.
+        *  Then it will execute SCRIPTS instruction until 
+        *  the next instruction that checks SCSI phase.
+        *  Then will stop and wait for selection to be 
+        *  complete or selection time-out to occur.
+        *
+        *  After having won arbitration, the SCRIPTS  
+        *  processor is able to execute instructions while 
+        *  the SCSI core is performing SCSI selection.
+        */
+
+       /*
+        *  Copy the CCB header to a fixed location 
+        *  in the HCB using self-modifying SCRIPTS.
+        */
+       SCR_COPY (4),
+               RADDR_1 (dsa),
+               PADDR_A (_sms_a30),
+       SCR_COPY (sizeof(struct sym_ccbh)),
+}/*-------------------------< _SMS_A30 >-------------------------*/,{
+               0,
+               HADDR_1 (ccb_head),
+       /*
+        *  Initialize the status register
+        */
+       SCR_COPY (4),
+               HADDR_1 (ccb_head.status),
+               RADDR_1 (scr0),
+}/*-------------------------< WF_SEL_DONE >----------------------*/,{
+       SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+               SIR_SEL_ATN_NO_MSG_OUT,
+}/*-------------------------< SEND_IDENT >-----------------------*/,{
+       /*
+        *  Selection complete.
+        *  Send the IDENTIFY and possibly the TAG message 
+        *  and negotiation message if present.
+        */
+       SCR_MOVE_TBL ^ SCR_MSG_OUT,
+               offsetof (struct sym_dsb, smsg),
+}/*-------------------------< SELECT2 >--------------------------*/,{
+#ifdef SYM_CONF_IARB_SUPPORT
+       /*
+        *  Set IMMEDIATE ARBITRATION if we have been given 
+        *  a hint to do so. (Some job to do after this one).
+        */
+       SCR_FROM_REG (HF_REG),
+               0,
+       SCR_JUMPR ^ IFFALSE (MASK (HF_HINT_IARB, HF_HINT_IARB)),
+               8,
+       SCR_REG_REG (scntl1, SCR_OR, IARB),
+               0,
+#endif
+       /*
+        *  Anticipate the COMMAND phase.
+        *  This is the PHASE we expect at this point.
+        */
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_COMMAND)),
+               PADDR_A (sel_no_cmd),
+}/*-------------------------< COMMAND >--------------------------*/,{
+       /*
+        *  ... and send the command
+        */
+       SCR_MOVE_TBL ^ SCR_COMMAND,
+               offsetof (struct sym_dsb, cmd),
+}/*-------------------------< DISPATCH >-------------------------*/,{
+       /*
+        *  MSG_IN is the only phase that shall be 
+        *  entered at least once for each (re)selection.
+        *  So we test it first.
+        */
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+               PADDR_A (msg_in),
+       SCR_JUMP ^ IFTRUE (IF (SCR_DATA_OUT)),
+               PADDR_A (datao_phase),
+       SCR_JUMP ^ IFTRUE (IF (SCR_DATA_IN)),
+               PADDR_A (datai_phase),
+       SCR_JUMP ^ IFTRUE (IF (SCR_STATUS)),
+               PADDR_A (status),
+       SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND)),
+               PADDR_A (command),
+       SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)),
+               PADDR_B (msg_out),
+       /*
+        *  Discard as many illegal phases as 
+        *  required and tell the C code about.
+        */
+       SCR_JUMPR ^ IFFALSE (WHEN (SCR_ILG_OUT)),
+               16,
+       SCR_MOVE_ABS (1) ^ SCR_ILG_OUT,
+               HADDR_1 (scratch),
+       SCR_JUMPR ^ IFTRUE (WHEN (SCR_ILG_OUT)),
+               -16,
+       SCR_JUMPR ^ IFFALSE (WHEN (SCR_ILG_IN)),
+               16,
+       SCR_MOVE_ABS (1) ^ SCR_ILG_IN,
+               HADDR_1 (scratch),
+       SCR_JUMPR ^ IFTRUE (WHEN (SCR_ILG_IN)),
+               -16,
+       SCR_INT,
+               SIR_BAD_PHASE,
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< SEL_NO_CMD >-----------------------*/,{
+       /*
+        *  The target does not switch to command 
+        *  phase after IDENTIFY has been sent.
+        *
+        *  If it stays in MSG OUT phase send it 
+        *  the IDENTIFY again.
+        */
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+               PADDR_B (resend_ident),
+       /*
+        *  If target does not switch to MSG IN phase 
+        *  and we sent a negotiation, assert the 
+        *  failure immediately.
+        */
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+               PADDR_A (dispatch),
+       SCR_FROM_REG (HS_REG),
+               0,
+       SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
+               SIR_NEGO_FAILED,
+       /*
+        *  Jump to dispatcher.
+        */
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< INIT >-----------------------------*/,{
+       /*
+        *  Wait for the SCSI RESET signal to be 
+        *  inactive before restarting operations, 
+        *  since the chip may hang on SEL_ATN 
+        *  if SCSI RESET is active.
+        */
+       SCR_FROM_REG (sstat0),
+               0,
+       SCR_JUMPR ^ IFTRUE (MASK (IRST, IRST)),
+               -16,
+       SCR_JUMP,
+               PADDR_A (start),
+}/*-------------------------< CLRACK >---------------------------*/,{
+       /*
+        *  Terminate possible pending message phase.
+        */
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< DATAI_DONE >-----------------------*/,{
+       /*
+        *  Save current pointer to LASTP.
+        */
+       SCR_COPY (4),
+               RADDR_1 (temp),
+               HADDR_1 (ccb_head.lastp),
+       /*
+        *  If the SWIDE is not full, jump to dispatcher.
+        *  We anticipate a STATUS phase.
+        */
+       SCR_FROM_REG (scntl2),
+               0,
+       SCR_JUMP ^ IFTRUE (MASK (WSR, WSR)),
+               PADDR_A (datai_done_wsr),
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_STATUS)),
+               PADDR_A (status),
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< DATAI_DONE_WSR >-------------------*/,{
+       /*
+        *  The SWIDE is full.
+        *  Clear this condition.
+        */
+       SCR_REG_REG (scntl2, SCR_OR, WSR),
+               0,
+       /*
+        *  We are expecting an IGNORE RESIDUE message 
+        *  from the device, otherwise we are in data 
+        *  overrun condition. Check against MSG_IN phase.
+        */
+       SCR_INT ^ IFFALSE (WHEN (SCR_MSG_IN)),
+               SIR_SWIDE_OVERRUN,
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+               PADDR_A (dispatch),
+       /*
+        *  We are in MSG_IN phase,
+        *  Read the first byte of the message.
+        *  If it is not an IGNORE RESIDUE message,
+        *  signal overrun and jump to message 
+        *  processing.
+        */
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               HADDR_1 (msgin[0]),
+       SCR_INT ^ IFFALSE (DATA (M_IGN_RESIDUE)),
+               SIR_SWIDE_OVERRUN,
+       SCR_JUMP ^ IFFALSE (DATA (M_IGN_RESIDUE)),
+               PADDR_A (msg_in2),
+       /*
+        *  We got the message we expected.
+        *  Read the 2nd byte, and jump to dispatcher.
+        */
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               HADDR_1 (msgin[1]),
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< DATAO_DONE >-----------------------*/,{
+       /*
+        *  Save current pointer to LASTP.
+        */
+       SCR_COPY (4),
+               RADDR_1 (temp),
+               HADDR_1 (ccb_head.lastp),
+       /*
+        *  If the SODL is not full jump to dispatcher.
+        *  We anticipate a STATUS phase.
+        */
+       SCR_FROM_REG (scntl2),
+               0,
+       SCR_JUMP ^ IFTRUE (MASK (WSS, WSS)),
+               PADDR_A (datao_done_wss),
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_STATUS)),
+               PADDR_A (status),
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< DATAO_DONE_WSS >-------------------*/,{
+       /*
+        *  The SODL is full, clear this condition.
+        */
+       SCR_REG_REG (scntl2, SCR_OR, WSS),
+               0,
+       /*
+        *  And signal a DATA UNDERRUN condition 
+        *  to the C code.
+        */
+       SCR_INT,
+               SIR_SODL_UNDERRUN,
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< DATAI_PHASE >----------------------*/,{
+       /*
+        *  Jump to current pointer.
+        */
+       SCR_COPY (4),
+               HADDR_1 (ccb_head.lastp),
+               RADDR_1 (temp),
+       SCR_RETURN,
+               0,
+}/*-------------------------< DATAO_PHASE >----------------------*/,{
+       /*
+        *  Jump to current pointer.
+        */
+       SCR_COPY (4),
+               HADDR_1 (ccb_head.lastp),
+               RADDR_1 (temp),
+       SCR_RETURN,
+               0,
+}/*-------------------------< MSG_IN >---------------------------*/,{
+       /*
+        *  Get the first byte of the message.
+        *
+        *  The script processor doesn't negate the
+        *  ACK signal after this transfer.
+        */
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               HADDR_1 (msgin[0]),
+}/*-------------------------< MSG_IN2 >--------------------------*/,{
+       /*
+        *  Check first against 1 byte messages 
+        *  that we handle from SCRIPTS.
+        */
+       SCR_JUMP ^ IFTRUE (DATA (M_COMPLETE)),
+               PADDR_A (complete),
+       SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)),
+               PADDR_A (disconnect),
+       SCR_JUMP ^ IFTRUE (DATA (M_SAVE_DP)),
+               PADDR_A (save_dp),
+       SCR_JUMP ^ IFTRUE (DATA (M_RESTORE_DP)),
+               PADDR_A (restore_dp),
+       /*
+        *  We handle all other messages from the 
+        *  C code, so no need to waste on-chip RAM 
+        *  for those ones.
+        */
+       SCR_JUMP,
+               PADDR_B (msg_in_etc),
+}/*-------------------------< STATUS >---------------------------*/,{
+       /*
+        *  get the status
+        */
+       SCR_MOVE_ABS (1) ^ SCR_STATUS,
+               HADDR_1 (scratch),
+#ifdef SYM_CONF_IARB_SUPPORT
+       /*
+        *  If STATUS is not GOOD, clear IMMEDIATE ARBITRATION, 
+        *  since we may have to tamper the start queue from 
+        *  the C code.
+        */
+       SCR_JUMPR ^ IFTRUE (DATA (S_GOOD)),
+               8,
+       SCR_REG_REG (scntl1, SCR_AND, ~IARB),
+               0,
+#endif
+       /*
+        *  save status to scsi_status.
+        *  mark as complete.
+        */
+       SCR_TO_REG (SS_REG),
+               0,
+       SCR_LOAD_REG (HS_REG, HS_COMPLETE),
+               0,
+       /*
+        *  Anticipate the MESSAGE PHASE for 
+        *  the TASK COMPLETE message.
+        */
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+               PADDR_A (msg_in),
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< COMPLETE >-------------------------*/,{
+       /*
+        *  Complete message.
+        *
+        *  When we terminate the cycle by clearing ACK,
+        *  the target may disconnect immediately.
+        *
+        *  We don't want to be told of an "unexpected disconnect",
+        *  so we disable this feature.
+        */
+       SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+               0,
+       /*
+        *  Terminate cycle ...
+        */
+       SCR_CLR (SCR_ACK|SCR_ATN),
+               0,
+       /*
+        *  ... and wait for the disconnect.
+        */
+       SCR_WAIT_DISC,
+               0,
+}/*-------------------------< COMPLETE2 >------------------------*/,{
+       /*
+        *  Save host status.
+        */
+       SCR_COPY (4),
+               RADDR_1 (scr0),
+               HADDR_1 (ccb_head.status),
+       /*
+        *  Move back the CCB header using self-modifying 
+        *  SCRIPTS.
+        */
+       SCR_COPY (4),
+               RADDR_1 (dsa),
+               PADDR_A (_sms_a40),
+       SCR_COPY (sizeof(struct sym_ccbh)),
+               HADDR_1 (ccb_head),
+}/*-------------------------< _SMS_A40 >-------------------------*/,{
+               0,
+       /*
+        *  Some bridges may reorder DMA writes to memory.
+        *  We donnot want the CPU to deal with completions  
+        *  without all the posted write having been flushed 
+        *  to memory. This DUMMY READ should flush posted 
+        *  buffers prior to the CPU having to deal with 
+        *  completions.
+        */
+       SCR_COPY (4),                   /* DUMMY READ */
+               HADDR_1 (ccb_head.status),
+               RADDR_1 (scr0),
+       /*
+        *  If command resulted in not GOOD status,
+        *  call the C code if needed.
+        */
+       SCR_FROM_REG (SS_REG),
+               0,
+       SCR_CALL ^ IFFALSE (DATA (S_GOOD)),
+               PADDR_B (bad_status),
+       /*
+        *  If we performed an auto-sense, call 
+        *  the C code to synchronyze task aborts 
+        *  with UNIT ATTENTION conditions.
+        */
+       SCR_FROM_REG (HF_REG),
+               0,
+       SCR_JUMP ^ IFFALSE (MASK (0 ,(HF_SENSE|HF_EXT_ERR))),
+               PADDR_A (complete_error),
+}/*-------------------------< DONE >-----------------------------*/,{
+       /*
+        *  Copy the DSA to the DONE QUEUE and 
+        *  signal completion to the host.
+        *  If we are interrupted between DONE 
+        *  and DONE_END, we must reset, otherwise 
+        *  the completed CCB may be lost.
+        */
+       SCR_COPY (4),
+               PADDR_B (done_pos),
+               PADDR_A (_sms_a50),
+       SCR_COPY (4),
+               RADDR_1 (dsa),
+}/*-------------------------< _SMS_A50 >-------------------------*/,{
+               0,
+       SCR_COPY (4),
+               PADDR_B (done_pos),
+               PADDR_A (_sms_a60),
+       /*
+        *  The instruction below reads the DONE QUEUE next 
+        *  free position from memory.
+        *  In addition it ensures that all PCI posted writes  
+        *  are flushed and so the DSA value of the done 
+        *  CCB is visible by the CPU before INTFLY is raised.
+        */
+       SCR_COPY (8),
+}/*-------------------------< _SMS_A60 >-------------------------*/,{
+               0,
+               PADDR_B (prev_done),
+}/*-------------------------< DONE_END >-------------------------*/,{
+       SCR_INT_FLY,
+               0,
+       SCR_JUMP,
+               PADDR_A (start),
+}/*-------------------------< COMPLETE_ERROR >-------------------*/,{
+       SCR_COPY (4),
+               PADDR_B (startpos),
+               RADDR_1 (scratcha),
+       SCR_INT,
+               SIR_COMPLETE_ERROR,
+}/*-------------------------< SAVE_DP >--------------------------*/,{
+       /*
+        *  Clear ACK immediately.
+        *  No need to delay it.
+        */
+       SCR_CLR (SCR_ACK),
+               0,
+       /*
+        *  Keep track we received a SAVE DP, so 
+        *  we will switch to the other PM context 
+        *  on the next PM since the DP may point 
+        *  to the current PM context.
+        */
+       SCR_REG_REG (HF_REG, SCR_OR, HF_DP_SAVED),
+               0,
+       /*
+        *  SAVE_DP message:
+        *  Copy LASTP to SAVEP.
+        */
+       SCR_COPY (4),
+               HADDR_1 (ccb_head.lastp),
+               HADDR_1 (ccb_head.savep),
+       /*
+        *  Anticipate the MESSAGE PHASE for 
+        *  the DISCONNECT message.
+        */
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+               PADDR_A (msg_in),
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< RESTORE_DP >-----------------------*/,{
+       /*
+        *  Clear ACK immediately.
+        *  No need to delay it.
+        */
+       SCR_CLR (SCR_ACK),
+               0,
+       /*
+        *  Copy SAVEP to LASTP.
+        */
+       SCR_COPY (4),
+               HADDR_1 (ccb_head.savep),
+               HADDR_1 (ccb_head.lastp),
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< DISCONNECT >-----------------------*/,{
+       /*
+        *  DISCONNECTing  ...
+        *
+        *  disable the "unexpected disconnect" feature,
+        *  and remove the ACK signal.
+        */
+       SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+               0,
+       SCR_CLR (SCR_ACK|SCR_ATN),
+               0,
+       /*
+        *  Wait for the disconnect.
+        */
+       SCR_WAIT_DISC,
+               0,
+       /*
+        *  Status is: DISCONNECTED.
+        */
+       SCR_LOAD_REG (HS_REG, HS_DISCONNECT),
+               0,
+       /*
+        *  Save host status.
+        */
+       SCR_COPY (4),
+               RADDR_1 (scr0),
+               HADDR_1 (ccb_head.status),
+}/*-------------------------< DISCONNECT2 >----------------------*/,{
+       /*
+        *  Move back the CCB header using self-modifying 
+        *  SCRIPTS.
+        */
+       SCR_COPY (4),
+               RADDR_1 (dsa),
+               PADDR_A (_sms_a65),
+       SCR_COPY (sizeof(struct sym_ccbh)),
+               HADDR_1 (ccb_head),
+}/*-------------------------< _SMS_A65 >-------------------------*/,{
+               0,
+       SCR_JUMP,
+               PADDR_A (start),
+}/*-------------------------< IDLE >-----------------------------*/,{
+       /*
+        *  Nothing to do?
+        *  Switch the LED off and wait for reselect.
+        *  Will be patched with a NO_OP if LED
+        *  not needed or not desired.
+        */
+       SCR_REG_REG (gpreg, SCR_OR, 0x01),
+               0,
+#ifdef SYM_CONF_IARB_SUPPORT
+       SCR_JUMPR,
+               8,
+#endif
+}/*-------------------------< UNGETJOB >-------------------------*/,{
+#ifdef SYM_CONF_IARB_SUPPORT
+       /*
+        *  Set IMMEDIATE ARBITRATION, for the next time.
+        *  This will give us better chance to win arbitration 
+        *  for the job we just wanted to do.
+        */
+       SCR_REG_REG (scntl1, SCR_OR, IARB),
+               0,
+#endif
+       /*
+        *  We are not able to restart the SCRIPTS if we are 
+        *  interrupted and these instruction haven't been 
+        *  all executed. BTW, this is very unlikely to 
+        *  happen, but we check that from the C code.
+        */
+       SCR_LOAD_REG (dsa, 0xff),
+               0,
+       SCR_COPY (4),
+               RADDR_1 (scratcha),
+               PADDR_B (startpos),
+}/*-------------------------< RESELECT >-------------------------*/,{
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+       /*
+        *  Make sure we are in initiator mode.
+        */
+       SCR_CLR (SCR_TRG),
+               0,
+#endif
+       /*
+        *  Sleep waiting for a reselection.
+        */
+       SCR_WAIT_RESEL,
+               PADDR_A(start),
+}/*-------------------------< RESELECTED >-----------------------*/,{
+       /*
+        *  Switch the LED on.
+        *  Will be patched with a NO_OP if LED
+        *  not needed or not desired.
+        */
+       SCR_REG_REG (gpreg, SCR_AND, 0xfe),
+               0,
+       /*
+        *  load the target id into the sdid
+        */
+       SCR_REG_SFBR (ssid, SCR_AND, 0x8F),
+               0,
+       SCR_TO_REG (sdid),
+               0,
+       /*
+        *  Load the target control block address
+        */
+       SCR_COPY (4),
+               PADDR_B (targtbl),
+               RADDR_1 (dsa),
+       SCR_SFBR_REG (dsa, SCR_SHL, 0),
+               0,
+       SCR_REG_REG (dsa, SCR_SHL, 0),
+               0,
+       SCR_REG_REG (dsa, SCR_AND, 0x3c),
+               0,
+       SCR_COPY (4),
+               RADDR_1 (dsa),
+               PADDR_A (_sms_a70),
+       SCR_COPY (4),
+}/*-------------------------< _SMS_A70 >-------------------------*/,{
+               0,
+               RADDR_1 (dsa),
+       /*
+        *  Copy the TCB header to a fixed place in 
+        *  the HCB.
+        */
+       SCR_COPY (4),
+               RADDR_1 (dsa),
+               PADDR_A (_sms_a80),
+       SCR_COPY (sizeof(struct sym_tcbh)),
+}/*-------------------------< _SMS_A80 >-------------------------*/,{
+               0,
+               HADDR_1 (tcb_head),
+       /*
+        *  We expect MESSAGE IN phase.
+        *  If not, get help from the C code.
+        */
+       SCR_INT ^ IFFALSE (WHEN (SCR_MSG_IN)),
+               SIR_RESEL_NO_MSG_IN,
+}/*-------------------------< RESELECTED1 >----------------------*/,{
+       /*
+        *  Load the synchronous transfer registers.
+        */
+       SCR_COPY (1),
+               HADDR_1 (tcb_head.wval),
+               RADDR_1 (scntl3),
+       SCR_COPY (1),
+               HADDR_1 (tcb_head.sval),
+               RADDR_1 (sxfer),
+       /*
+        *  Get the IDENTIFY message.
+        */
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               HADDR_1 (msgin),
+       /*
+        *  If IDENTIFY LUN #0, use a faster path 
+        *  to find the LCB structure.
+        */
+       SCR_JUMP ^ IFTRUE (MASK (0x80, 0xbf)),
+               PADDR_A (resel_lun0),
+       /*
+        *  If message isn't an IDENTIFY, 
+        *  tell the C code about.
+        */
+       SCR_INT ^ IFFALSE (MASK (0x80, 0x80)),
+               SIR_RESEL_NO_IDENTIFY,
+       /*
+        *  It is an IDENTIFY message,
+        *  Load the LUN control block address.
+        */
+       SCR_COPY (4),
+               HADDR_1 (tcb_head.luntbl_sa),
+               RADDR_1 (dsa),
+       SCR_SFBR_REG (dsa, SCR_SHL, 0),
+               0,
+       SCR_REG_REG (dsa, SCR_SHL, 0),
+               0,
+       SCR_REG_REG (dsa, SCR_AND, 0xfc),
+               0,
+       SCR_COPY (4),
+               RADDR_1 (dsa),
+               PADDR_A (_sms_a90),
+       SCR_COPY (4),
+}/*-------------------------< _SMS_A90 >-------------------------*/,{
+               0,
+               RADDR_1 (dsa),
+       SCR_JUMPR,
+               12,
+}/*-------------------------< RESEL_LUN0 >-----------------------*/,{
+       /*
+        *  LUN 0 special case (but usual one :))
+        */
+       SCR_COPY (4),
+               HADDR_1 (tcb_head.lun0_sa),
+               RADDR_1 (dsa),
+       /*
+        *  Jump indirectly to the reselect action for this LUN.
+        *  (lcb.head.resel_sa assumed at offset zero of lcb).
+        */
+       SCR_COPY (4),
+               RADDR_1 (dsa),
+               PADDR_A (_sms_a100),
+       SCR_COPY (4),
+}/*-------------------------< _SMS_A100 >------------------------*/,{
+               0,
+               RADDR_1 (temp),
+       SCR_RETURN,
+               0,
+       /* In normal situations, we jump to RESEL_TAG or RESEL_NO_TAG */
+}/*-------------------------< RESEL_TAG >------------------------*/,{
+       /*
+        *  ACK the IDENTIFY previously received.
+        */
+       SCR_CLR (SCR_ACK),
+               0,
+       /*
+        *  It shall be a tagged command.
+        *  Read SIMPLE+TAG.
+        *  The C code will deal with errors.
+        *  Agressive optimization, is'nt it? :)
+        */
+       SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
+               HADDR_1 (msgin),
+       /*
+        *  Copy the LCB header to a fixed place in 
+        *  the HCB using self-modifying SCRIPTS.
+        */
+       SCR_COPY (4),
+               RADDR_1 (dsa),
+               PADDR_A (_sms_a110),
+       SCR_COPY (sizeof(struct sym_lcbh)),
+}/*-------------------------< _SMS_A110 >------------------------*/,{
+               0,
+               HADDR_1 (lcb_head),
+       /*
+        *  Load the pointer to the tagged task 
+        *  table for this LUN.
+        */
+       SCR_COPY (4),
+               HADDR_1 (lcb_head.itlq_tbl_sa),
+               RADDR_1 (dsa),
+       /*
+        *  The SIDL still contains the TAG value.
+        *  Agressive optimization, isn't it? :):)
+        */
+       SCR_REG_SFBR (sidl, SCR_SHL, 0),
+               0,
+#if SYM_CONF_MAX_TASK*4 > 512
+       SCR_JUMPR ^ IFFALSE (CARRYSET),
+               8,
+       SCR_REG_REG (dsa1, SCR_OR, 2),
+               0,
+       SCR_REG_REG (sfbr, SCR_SHL, 0),
+               0,
+       SCR_JUMPR ^ IFFALSE (CARRYSET),
+               8,
+       SCR_REG_REG (dsa1, SCR_OR, 1),
+               0,
+#elif SYM_CONF_MAX_TASK*4 > 256
+       SCR_JUMPR ^ IFFALSE (CARRYSET),
+               8,
+       SCR_REG_REG (dsa1, SCR_OR, 1),
+               0,
+#endif
+       /*
+        *  Retrieve the DSA of this task.
+        *  JUMP indirectly to the restart point of the CCB.
+        */
+       SCR_SFBR_REG (dsa, SCR_AND, 0xfc),
+               0,
+       SCR_COPY (4),
+               RADDR_1 (dsa),
+               PADDR_A (_sms_a120),
+       SCR_COPY (4),
+}/*-------------------------< _SMS_A120 >------------------------*/,{
+               0,
+               RADDR_1 (dsa),
+}/*-------------------------< RESEL_GO >-------------------------*/,{
+       SCR_COPY (4),
+               RADDR_1 (dsa),
+               PADDR_A (_sms_a130),
+       /*
+        *  Move 'ccb.phys.head.go' action to 
+        *  scratch/scratch1. So scratch1 will 
+        *  contain the 'restart' field of the 
+        *  'go' structure.
+        */
+       SCR_COPY (8),
+}/*-------------------------< _SMS_A130 >------------------------*/,{
+               0,
+               PADDR_B (scratch),
+       SCR_COPY (4),
+               PADDR_B (scratch1), /* phys.head.go.restart */
+               RADDR_1 (temp),
+       SCR_RETURN,
+               0,
+       /* In normal situations we branch to RESEL_DSA */
+}/*-------------------------< RESEL_DSA >------------------------*/,{
+       /*
+        *  ACK the IDENTIFY or TAG previously received.
+        */
+       SCR_CLR (SCR_ACK),
+               0,
+}/*-------------------------< RESEL_DSA1 >-----------------------*/,{
+       /*
+        *  Copy the CCB header to a fixed location 
+        *  in the HCB using self-modifying SCRIPTS.
+        */
+       SCR_COPY (4),
+               RADDR_1 (dsa),
+               PADDR_A (_sms_a140),
+       SCR_COPY (sizeof(struct sym_ccbh)),
+}/*-------------------------< _SMS_A140 >------------------------*/,{
+               0,
+               HADDR_1 (ccb_head),
+       /*
+        *  Initialize the status register
+        */
+       SCR_COPY (4),
+               HADDR_1 (ccb_head.status),
+               RADDR_1 (scr0),
+       /*
+        *  Jump to dispatcher.
+        */
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< RESEL_NO_TAG >---------------------*/,{
+       /*
+        *  Copy the LCB header to a fixed place in 
+        *  the HCB using self-modifying SCRIPTS.
+        */
+       SCR_COPY (4),
+               RADDR_1 (dsa),
+               PADDR_A (_sms_a145),
+       SCR_COPY (sizeof(struct sym_lcbh)),
+}/*-------------------------< _SMS_A145 >------------------------*/,{
+               0,
+               HADDR_1 (lcb_head),
+       /*
+        *  Load the DSA with the unique ITL task.
+        */
+       SCR_COPY (4),
+               HADDR_1 (lcb_head.itl_task_sa),
+               RADDR_1 (dsa),
+       SCR_JUMP,
+               PADDR_A (resel_go),
+}/*-------------------------< DATA_IN >--------------------------*/,{
+/*
+ *  Because the size depends on the
+ *  #define SYM_CONF_MAX_SG parameter,
+ *  it is filled in at runtime.
+ *
+ *  ##===========< i=0; i<SYM_CONF_MAX_SG >=========
+ *  || SCR_CHMOV_TBL ^ SCR_DATA_IN,
+ *  ||         offsetof (struct sym_dsb, data[ i]),
+ *  ##==========================================
+ */
+0
+}/*-------------------------< DATA_IN2 >-------------------------*/,{
+       SCR_CALL,
+               PADDR_A (datai_done),
+       SCR_JUMP,
+               PADDR_B (data_ovrun),
+}/*-------------------------< DATA_OUT >-------------------------*/,{
+/*
+ *  Because the size depends on the
+ *  #define SYM_CONF_MAX_SG parameter,
+ *  it is filled in at runtime.
+ *
+ *  ##===========< i=0; i<SYM_CONF_MAX_SG >=========
+ *  || SCR_CHMOV_TBL ^ SCR_DATA_OUT,
+ *  ||         offsetof (struct sym_dsb, data[ i]),
+ *  ##==========================================
+ */
+0
+}/*-------------------------< DATA_OUT2 >------------------------*/,{
+       SCR_CALL,
+               PADDR_A (datao_done),
+       SCR_JUMP,
+               PADDR_B (data_ovrun),
+}/*-------------------------< PM0_DATA >-------------------------*/,{
+       /*
+        *  Read our host flags to SFBR, so we will be able 
+        *  to check against the data direction we expect.
+        */
+       SCR_FROM_REG (HF_REG),
+               0,
+       /*
+        *  Check against actual DATA PHASE.
+        */
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+               PADDR_A (pm0_data_out),
+       /*
+        *  Actual phase is DATA IN.
+        *  Check against expected direction.
+        */
+       SCR_JUMP ^ IFFALSE (MASK (HF_DATA_IN, HF_DATA_IN)),
+               PADDR_B (data_ovrun),
+       /*
+        *  Keep track we are moving data from the 
+        *  PM0 DATA mini-script.
+        */
+       SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM0),
+               0,
+       /*
+        *  Move the data to memory.
+        */
+       SCR_CHMOV_TBL ^ SCR_DATA_IN,
+               offsetof (struct sym_ccb, phys.pm0.sg),
+       SCR_JUMP,
+               PADDR_A (pm0_data_end),
+}/*-------------------------< PM0_DATA_OUT >---------------------*/,{
+       /*
+        *  Actual phase is DATA OUT.
+        *  Check against expected direction.
+        */
+       SCR_JUMP ^ IFTRUE (MASK (HF_DATA_IN, HF_DATA_IN)),
+               PADDR_B (data_ovrun),
+       /*
+        *  Keep track we are moving data from the 
+        *  PM0 DATA mini-script.
+        */
+       SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM0),
+               0,
+       /*
+        *  Move the data from memory.
+        */
+       SCR_CHMOV_TBL ^ SCR_DATA_OUT,
+               offsetof (struct sym_ccb, phys.pm0.sg),
+}/*-------------------------< PM0_DATA_END >---------------------*/,{
+       /*
+        *  Clear the flag that told we were moving  
+        *  data from the PM0 DATA mini-script.
+        */
+       SCR_REG_REG (HF_REG, SCR_AND, (~HF_IN_PM0)),
+               0,
+       /*
+        *  Return to the previous DATA script which 
+        *  is guaranteed by design (if no bug) to be 
+        *  the main DATA script for this transfer.
+        */
+       SCR_COPY (4),
+               RADDR_1 (dsa),
+               RADDR_1 (scratcha),
+       SCR_REG_REG (scratcha, SCR_ADD, offsetof (struct sym_ccb,phys.pm0.ret)),
+               0,
+}/*-------------------------< PM_DATA_END >----------------------*/,{
+       SCR_COPY (4),
+               RADDR_1 (scratcha),
+               PADDR_A (_sms_a150),
+       SCR_COPY (4),
+}/*-------------------------< _SMS_A150 >------------------------*/,{
+               0,
+               RADDR_1 (temp),
+       SCR_RETURN,
+               0,
+}/*-------------------------< PM1_DATA >-------------------------*/,{
+       /*
+        *  Read our host flags to SFBR, so we will be able 
+        *  to check against the data direction we expect.
+        */
+       SCR_FROM_REG (HF_REG),
+               0,
+       /*
+        *  Check against actual DATA PHASE.
+        */
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+               PADDR_A (pm1_data_out),
+       /*
+        *  Actual phase is DATA IN.
+        *  Check against expected direction.
+        */
+       SCR_JUMP ^ IFFALSE (MASK (HF_DATA_IN, HF_DATA_IN)),
+               PADDR_B (data_ovrun),
+       /*
+        *  Keep track we are moving data from the 
+        *  PM1 DATA mini-script.
+        */
+       SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM1),
+               0,
+       /*
+        *  Move the data to memory.
+        */
+       SCR_CHMOV_TBL ^ SCR_DATA_IN,
+               offsetof (struct sym_ccb, phys.pm1.sg),
+       SCR_JUMP,
+               PADDR_A (pm1_data_end),
+}/*-------------------------< PM1_DATA_OUT >---------------------*/,{
+       /*
+        *  Actual phase is DATA OUT.
+        *  Check against expected direction.
+        */
+       SCR_JUMP ^ IFTRUE (MASK (HF_DATA_IN, HF_DATA_IN)),
+               PADDR_B (data_ovrun),
+       /*
+        *  Keep track we are moving data from the 
+        *  PM1 DATA mini-script.
+        */
+       SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM1),
+               0,
+       /*
+        *  Move the data from memory.
+        */
+       SCR_CHMOV_TBL ^ SCR_DATA_OUT,
+               offsetof (struct sym_ccb, phys.pm1.sg),
+}/*-------------------------< PM1_DATA_END >---------------------*/,{
+       /*
+        *  Clear the flag that told we were moving  
+        *  data from the PM1 DATA mini-script.
+        */
+       SCR_REG_REG (HF_REG, SCR_AND, (~HF_IN_PM1)),
+               0,
+       /*
+        *  Return to the previous DATA script which 
+        *  is guaranteed by design (if no bug) to be 
+        *  the main DATA script for this transfer.
+        */
+       SCR_COPY (4),
+               RADDR_1 (dsa),
+               RADDR_1 (scratcha),
+       SCR_REG_REG (scratcha, SCR_ADD, offsetof (struct sym_ccb,phys.pm1.ret)),
+               0,
+       SCR_JUMP,
+               PADDR_A (pm_data_end),
+}/*--------------------------<>----------------------------------*/
+};
+
+static struct SYM_FWB_SCR SYM_FWB_SCR = {
+/*-------------------------< NO_DATA >--------------------------*/ {
+       SCR_JUMP,
+               PADDR_B (data_ovrun),
+}/*-------------------------< SEL_FOR_ABORT >--------------------*/,{
+       /*
+        *  We are jumped here by the C code, if we have 
+        *  some target to reset or some disconnected 
+        *  job to abort. Since error recovery is a serious 
+        *  busyness, we will really reset the SCSI BUS, if 
+        *  case of a SCSI interrupt occuring in this path.
+        */
+
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+       /*
+        *  Set initiator mode.
+        */
+       SCR_CLR (SCR_TRG),
+               0,
+#endif
+       /*
+        *      And try to select this target.
+        */
+       SCR_SEL_TBL_ATN ^ offsetof (struct sym_hcb, abrt_sel),
+               PADDR_A (reselect),
+       /*
+        *  Wait for the selection to complete or 
+        *  the selection to time out.
+        */
+       SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+               -8,
+       /*
+        *  Call the C code.
+        */
+       SCR_INT,
+               SIR_TARGET_SELECTED,
+       /*
+        *  The C code should let us continue here. 
+        *  Send the 'kiss of death' message.
+        *  We expect an immediate disconnect once 
+        *  the target has eaten the message.
+        */
+       SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+               0,
+       SCR_MOVE_TBL ^ SCR_MSG_OUT,
+               offsetof (struct sym_hcb, abrt_tbl),
+       SCR_CLR (SCR_ACK|SCR_ATN),
+               0,
+       SCR_WAIT_DISC,
+               0,
+       /*
+        *  Tell the C code that we are done.
+        */
+       SCR_INT,
+               SIR_ABORT_SENT,
+}/*-------------------------< SEL_FOR_ABORT_1 >------------------*/,{
+       /*
+        *  Jump at scheduler.
+        */
+       SCR_JUMP,
+               PADDR_A (start),
+}/*-------------------------< MSG_IN_ETC >-----------------------*/,{
+       /*
+        *  If it is an EXTENDED (variable size message)
+        *  Handle it.
+        */
+       SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
+               PADDR_B (msg_extended),
+       /*
+        *  Let the C code handle any other 
+        *  1 byte message.
+        */
+       SCR_JUMP ^ IFTRUE (MASK (0x00, 0xf0)),
+               PADDR_B (msg_received),
+       SCR_JUMP ^ IFTRUE (MASK (0x10, 0xf0)),
+               PADDR_B (msg_received),
+       /*
+        *  We donnot handle 2 bytes messages from SCRIPTS.
+        *  So, let the C code deal with these ones too.
+        */
+       SCR_JUMP ^ IFFALSE (MASK (0x20, 0xf0)),
+               PADDR_B (msg_weird_seen),
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               HADDR_1 (msgin[1]),
+}/*-------------------------< MSG_RECEIVED >---------------------*/,{
+       SCR_COPY (4),                   /* DUMMY READ */
+               HADDR_1 (scratch),
+               RADDR_1 (scratcha),
+       SCR_INT,
+               SIR_MSG_RECEIVED,
+}/*-------------------------< MSG_WEIRD_SEEN >-------------------*/,{
+       SCR_COPY (4),                   /* DUMMY READ */
+               HADDR_1 (scratch),
+               RADDR_1 (scratcha),
+       SCR_INT,
+               SIR_MSG_WEIRD,
+}/*-------------------------< MSG_EXTENDED >---------------------*/,{
+       /*
+        *  Clear ACK and get the next byte 
+        *  assumed to be the message length.
+        */
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               HADDR_1 (msgin[1]),
+       /*
+        *  Try to catch some unlikely situations as 0 length 
+        *  or too large the length.
+        */
+       SCR_JUMP ^ IFTRUE (DATA (0)),
+               PADDR_B (msg_weird_seen),
+       SCR_TO_REG (scratcha),
+               0,
+       SCR_REG_REG (sfbr, SCR_ADD, (256-8)),
+               0,
+       SCR_JUMP ^ IFTRUE (CARRYSET),
+               PADDR_B (msg_weird_seen),
+       /*
+        *  We donnot handle extended messages from SCRIPTS.
+        *  Read the amount of data correponding to the 
+        *  message length and call the C code.
+        */
+       SCR_COPY (1),
+               RADDR_1 (scratcha),
+               PADDR_B (_sms_b10),
+       SCR_CLR (SCR_ACK),
+               0,
+}/*-------------------------< _SMS_B10 >-------------------------*/,{
+       SCR_MOVE_ABS (0) ^ SCR_MSG_IN,
+               HADDR_1 (msgin[2]),
+       SCR_JUMP,
+               PADDR_B (msg_received),
+}/*-------------------------< MSG_BAD >--------------------------*/,{
+       /*
+        *  unimplemented message - reject it.
+        */
+       SCR_INT,
+               SIR_REJECT_TO_SEND,
+       SCR_SET (SCR_ATN),
+               0,
+       SCR_JUMP,
+               PADDR_A (clrack),
+}/*-------------------------< MSG_WEIRD >------------------------*/,{
+       /*
+        *  weird message received
+        *  ignore all MSG IN phases and reject it.
+        */
+       SCR_INT,
+               SIR_REJECT_TO_SEND,
+       SCR_SET (SCR_ATN),
+               0,
+}/*-------------------------< MSG_WEIRD1 >-----------------------*/,{
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+               PADDR_A (dispatch),
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               HADDR_1 (scratch),
+       SCR_JUMP,
+               PADDR_B (msg_weird1),
+}/*-------------------------< WDTR_RESP >------------------------*/,{
+       /*
+        *  let the target fetch our answer.
+        */
+       SCR_SET (SCR_ATN),
+               0,
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+               PADDR_B (nego_bad_phase),
+}/*-------------------------< SEND_WDTR >------------------------*/,{
+       /*
+        *  Send the M_X_WIDE_REQ
+        */
+       SCR_MOVE_ABS (4) ^ SCR_MSG_OUT,
+               HADDR_1 (msgout),
+       SCR_JUMP,
+               PADDR_B (msg_out_done),
+}/*-------------------------< SDTR_RESP >------------------------*/,{
+       /*
+        *  let the target fetch our answer.
+        */
+       SCR_SET (SCR_ATN),
+               0,
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+               PADDR_B (nego_bad_phase),
+}/*-------------------------< SEND_SDTR >------------------------*/,{
+       /*
+        *  Send the M_X_SYNC_REQ
+        */
+       SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
+               HADDR_1 (msgout),
+       SCR_JUMP,
+               PADDR_B (msg_out_done),
+}/*-------------------------< PPR_RESP >-------------------------*/,{
+       /*
+        *  let the target fetch our answer.
+        */
+       SCR_SET (SCR_ATN),
+               0,
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+               PADDR_B (nego_bad_phase),
+}/*-------------------------< SEND_PPR >-------------------------*/,{
+       /*
+        *  Send the M_X_PPR_REQ
+        */
+       SCR_MOVE_ABS (8) ^ SCR_MSG_OUT,
+               HADDR_1 (msgout),
+       SCR_JUMP,
+               PADDR_B (msg_out_done),
+}/*-------------------------< NEGO_BAD_PHASE >-------------------*/,{
+       SCR_INT,
+               SIR_NEGO_PROTO,
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< MSG_OUT >--------------------------*/,{
+       /*
+        *  The target requests a message.
+        *  We donnot send messages that may 
+        *  require the device to go to bus free.
+        */
+       SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+               HADDR_1 (msgout),
+       /*
+        *  ... wait for the next phase
+        *  if it's a message out, send it again, ...
+        */
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+               PADDR_B (msg_out),
+}/*-------------------------< MSG_OUT_DONE >---------------------*/,{
+       /*
+        *  Let the C code be aware of the 
+        *  sent message and clear the message.
+        */
+       SCR_INT,
+               SIR_MSG_OUT_DONE,
+       /*
+        *  ... and process the next phase
+        */
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< DATA_OVRUN >-----------------------*/,{
+       /*
+        *  Zero scratcha that will count the 
+        *  extras bytes.
+        */
+       SCR_COPY (4),
+               PADDR_B (zero),
+               RADDR_1 (scratcha),
+}/*-------------------------< DATA_OVRUN1 >----------------------*/,{
+       /*
+        *  The target may want to transfer too much data.
+        *
+        *  If phase is DATA OUT write 1 byte and count it.
+        */
+       SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_OUT)),
+               16,
+       SCR_CHMOV_ABS (1) ^ SCR_DATA_OUT,
+               HADDR_1 (scratch),
+       SCR_JUMP,
+               PADDR_B (data_ovrun2),
+       /*
+        *  If WSR is set, clear this condition, and 
+        *  count this byte.
+        */
+       SCR_FROM_REG (scntl2),
+               0,
+       SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)),
+               16,
+       SCR_REG_REG (scntl2, SCR_OR, WSR),
+               0,
+       SCR_JUMP,
+               PADDR_B (data_ovrun2),
+       /*
+        *  Finally check against DATA IN phase.
+        *  Signal data overrun to the C code 
+        *  and jump to dispatcher if not so.
+        *  Read 1 byte otherwise and count it.
+        */
+       SCR_JUMPR ^ IFTRUE (WHEN (SCR_DATA_IN)),
+               16,
+       SCR_INT,
+               SIR_DATA_OVERRUN,
+       SCR_JUMP,
+               PADDR_A (dispatch),
+       SCR_CHMOV_ABS (1) ^ SCR_DATA_IN,
+               HADDR_1 (scratch),
+}/*-------------------------< DATA_OVRUN2 >----------------------*/,{
+       /*
+        *  Count this byte.
+        *  This will allow to return a negative 
+        *  residual to user.
+        */
+       SCR_REG_REG (scratcha,  SCR_ADD,  0x01),
+               0,
+       SCR_REG_REG (scratcha1, SCR_ADDC, 0),
+               0,
+       SCR_REG_REG (scratcha2, SCR_ADDC, 0),
+               0,
+       /*
+        *  .. and repeat as required.
+        */
+       SCR_JUMP,
+               PADDR_B (data_ovrun1),
+}/*-------------------------< ABORT_RESEL >----------------------*/,{
+       SCR_SET (SCR_ATN),
+               0,
+       SCR_CLR (SCR_ACK),
+               0,
+       /*
+        *  send the abort/abortag/reset message
+        *  we expect an immediate disconnect
+        */
+       SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+               0,
+       SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+               HADDR_1 (msgout),
+       SCR_CLR (SCR_ACK|SCR_ATN),
+               0,
+       SCR_WAIT_DISC,
+               0,
+       SCR_INT,
+               SIR_RESEL_ABORTED,
+       SCR_JUMP,
+               PADDR_A (start),
+}/*-------------------------< RESEND_IDENT >---------------------*/,{
+       /*
+        *  The target stays in MSG OUT phase after having acked 
+        *  Identify [+ Tag [+ Extended message ]]. Targets shall
+        *  behave this way on parity error.
+        *  We must send it again all the messages.
+        */
+       SCR_SET (SCR_ATN), /* Shall be asserted 2 deskew delays before the  */
+               0,         /* 1rst ACK = 90 ns. Hope the chip isn't too fast */
+       SCR_JUMP,
+               PADDR_A (send_ident),
+}/*-------------------------< IDENT_BREAK >----------------------*/,{
+       SCR_CLR (SCR_ATN),
+               0,
+       SCR_JUMP,
+               PADDR_A (select2),
+}/*-------------------------< IDENT_BREAK_ATN >------------------*/,{
+       SCR_SET (SCR_ATN),
+               0,
+       SCR_JUMP,
+               PADDR_A (select2),
+}/*-------------------------< SDATA_IN >-------------------------*/,{
+       SCR_CHMOV_TBL ^ SCR_DATA_IN,
+               offsetof (struct sym_dsb, sense),
+       SCR_CALL,
+               PADDR_A (datai_done),
+       SCR_JUMP,
+               PADDR_B (data_ovrun),
+}/*-------------------------< RESEL_BAD_LUN >--------------------*/,{
+       /*
+        *  Message is an IDENTIFY, but lun is unknown.
+        *  Signal problem to C code for logging the event.
+        *  Send a M_ABORT to clear all pending tasks.
+        */
+       SCR_INT,
+               SIR_RESEL_BAD_LUN,
+       SCR_JUMP,
+               PADDR_B (abort_resel),
+}/*-------------------------< BAD_I_T_L >------------------------*/,{
+       /*
+        *  We donnot have a task for that I_T_L.
+        *  Signal problem to C code for logging the event.
+        *  Send a M_ABORT message.
+        */
+       SCR_INT,
+               SIR_RESEL_BAD_I_T_L,
+       SCR_JUMP,
+               PADDR_B (abort_resel),
+}/*-------------------------< BAD_I_T_L_Q >----------------------*/,{
+       /*
+        *  We donnot have a task that matches the tag.
+        *  Signal problem to C code for logging the event.
+        *  Send a M_ABORTTAG message.
+        */
+       SCR_INT,
+               SIR_RESEL_BAD_I_T_L_Q,
+       SCR_JUMP,
+               PADDR_B (abort_resel),
+}/*-------------------------< BAD_STATUS >-----------------------*/,{
+       /*
+        *  Anything different from INTERMEDIATE 
+        *  CONDITION MET should be a bad SCSI status, 
+        *  given that GOOD status has already been tested.
+        *  Call the C code.
+        */
+       SCR_COPY (4),
+               PADDR_B (startpos),
+               RADDR_1 (scratcha),
+       SCR_INT ^ IFFALSE (DATA (S_COND_MET)),
+               SIR_BAD_SCSI_STATUS,
+       SCR_RETURN,
+               0,
+}/*-------------------------< WSR_MA_HELPER >--------------------*/,{
+       /*
+        *  Helper for the C code when WSR bit is set.
+        *  Perform the move of the residual byte.
+        */
+       SCR_CHMOV_TBL ^ SCR_DATA_IN,
+               offsetof (struct sym_ccb, phys.wresid),
+       SCR_JUMP,
+               PADDR_A (dispatch),
+
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+}/*-------------------------< DATA_IO >--------------------------*/,{
+       /*
+        *  We jump here if the data direction was unknown at the 
+        *  time we had to queue the command to the scripts processor.
+        *  Pointers had been set as follow in this situation:
+        *    savep   -->   DATA_IO
+        *    lastp   -->   start pointer when DATA_IN
+        *    wlastp  -->   start pointer when DATA_OUT
+        *  This script sets savep and lastp according to the 
+        *  direction chosen by the target.
+        */
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_DATA_OUT)),
+               PADDR_B (data_io_out),
+}/*-------------------------< DATA_IO_COM >----------------------*/,{
+       /*
+        *  Direction is DATA IN.
+        */
+       SCR_COPY  (4),
+               HADDR_1 (ccb_head.lastp),
+               HADDR_1 (ccb_head.savep),
+       /*
+        *  Jump to the SCRIPTS according to actual direction.
+        */
+       SCR_COPY  (4),
+               HADDR_1 (ccb_head.savep),
+               RADDR_1 (temp),
+       SCR_RETURN,
+               0,
+}/*-------------------------< DATA_IO_OUT >----------------------*/,{
+       /*
+        *  Direction is DATA OUT.
+        */
+       SCR_REG_REG (HF_REG, SCR_AND, (~HF_DATA_IN)),
+               0,
+       SCR_COPY  (4),
+               HADDR_1 (ccb_head.wlastp),
+               HADDR_1 (ccb_head.lastp),
+       SCR_JUMP,
+               PADDR_B(data_io_com),
+#endif /* SYM_OPT_HANDLE_DIR_UNKNOWN */
+
+}/*-------------------------< ZERO >-----------------------------*/,{
+       SCR_DATA_ZERO,
+}/*-------------------------< SCRATCH >--------------------------*/,{
+       SCR_DATA_ZERO, /* MUST BE BEFORE SCRATCH1 */
+}/*-------------------------< SCRATCH1 >-------------------------*/,{
+       SCR_DATA_ZERO,
+}/*-------------------------< PREV_DONE >------------------------*/,{
+       SCR_DATA_ZERO, /* MUST BE BEFORE DONE_POS ! */
+}/*-------------------------< DONE_POS >-------------------------*/,{
+       SCR_DATA_ZERO,
+}/*-------------------------< NEXTJOB >--------------------------*/,{
+       SCR_DATA_ZERO, /* MUST BE BEFORE STARTPOS ! */
+}/*-------------------------< STARTPOS >-------------------------*/,{
+       SCR_DATA_ZERO,
+}/*-------------------------< TARGTBL >--------------------------*/,{
+       SCR_DATA_ZERO,
+}/*--------------------------<>----------------------------------*/
+};
+
+static struct SYM_FWZ_SCR SYM_FWZ_SCR = {
+ /*-------------------------< SNOOPTEST >------------------------*/{
+       /*
+        *  Read the variable.
+        */
+       SCR_COPY (4),
+               HADDR_1 (scratch),
+               RADDR_1 (scratcha),
+       /*
+        *  Write the variable.
+        */
+       SCR_COPY (4),
+               RADDR_1 (temp),
+               HADDR_1 (scratch),
+       /*
+        *  Read back the variable.
+        */
+       SCR_COPY (4),
+               HADDR_1 (scratch),
+               RADDR_1 (temp),
+}/*-------------------------< SNOOPEND >-------------------------*/,{
+       /*
+        *  And stop.
+        */
+       SCR_INT,
+               99,
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+       /*
+        *  We may use MEMORY MOVE instructions to load the on chip-RAM,
+        *  if it happens that mapping PCI memory is not possible.
+        *  But writing the RAM from the CPU is the preferred method, 
+        *  since PCI 2.2 seems to disallow PCI self-mastering.
+        */
+}/*-------------------------< START_RAM >------------------------*/,{
+       /*
+        *  Load the script into on-chip RAM, 
+        *  and jump to start point.
+        */
+       SCR_COPY (sizeof(struct SYM_FWA_SCR)),
+}/*-------------------------< SCRIPTA0_BA >----------------------*/,{
+               0,
+               PADDR_A (start),
+       SCR_JUMP,
+               PADDR_A (init),
+#endif /* SYM_OPT_NO_BUS_MEMORY_MAPPING */
+}/*--------------------------<>----------------------------------*/
+};
diff --git a/xen/drivers/scsi/sym53c8xx_2/sym_fw2.h b/xen/drivers/scsi/sym53c8xx_2/sym_fw2.h
new file mode 100644 (file)
index 0000000..e494e17
--- /dev/null
@@ -0,0 +1,1994 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000  Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ *         Wolfgang Stanglmeier        <wolf@cologne.de>
+ *         Stefan Esser                <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994  Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ *  Scripts for SYMBIOS-Processor
+ *
+ *  We have to know the offsets of all labels before we reach 
+ *  them (for forward jumps). Therefore we declare a struct 
+ *  here. If you make changes inside the script,
+ *
+ *  DONT FORGET TO CHANGE THE LENGTHS HERE!
+ */
+
+/*
+ *  Script fragments which are loaded into the on-chip RAM 
+ *  of 825A, 875, 876, 895, 895A, 896 and 1010 chips.
+ *  Must not exceed 4K bytes.
+ */
+struct SYM_FWA_SCR {
+       u32 start               [ 14];
+       u32 getjob_begin        [  4];
+       u32 getjob_end          [  4];
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+       u32 select              [  6];
+#else
+       u32 select              [  4];
+#endif
+#if    SYM_CONF_DMA_ADDRESSING_MODE == 2
+       u32 is_dmap_dirty       [  4];
+#endif
+       u32 wf_sel_done         [  2];
+       u32 sel_done            [  2];
+       u32 send_ident          [  2];
+#ifdef SYM_CONF_IARB_SUPPORT
+       u32 select2             [  8];
+#else
+       u32 select2             [  2];
+#endif
+       u32 command             [  2];
+       u32 dispatch            [ 28];
+       u32 sel_no_cmd          [ 10];
+       u32 init                [  6];
+       u32 clrack              [  4];
+       u32 datai_done          [ 10];
+       u32 datai_done_wsr      [ 20];
+       u32 datao_done          [ 10];
+       u32 datao_done_wss      [  6];
+       u32 datai_phase         [  4];
+       u32 datao_phase         [  6];
+       u32 msg_in              [  2];
+       u32 msg_in2             [ 10];
+#ifdef SYM_CONF_IARB_SUPPORT
+       u32 status              [ 14];
+#else
+       u32 status              [ 10];
+#endif
+       u32 complete            [  6];
+       u32 complete2           [ 12];
+       u32 done                [ 14];
+       u32 done_end            [  2];
+       u32 complete_error      [  4];
+       u32 save_dp             [ 12];
+       u32 restore_dp          [  8];
+       u32 disconnect          [ 12];
+#ifdef SYM_CONF_IARB_SUPPORT
+       u32 idle                [  4];
+#else
+       u32 idle                [  2];
+#endif
+#ifdef SYM_CONF_IARB_SUPPORT
+       u32 ungetjob            [  6];
+#else
+       u32 ungetjob            [  4];
+#endif
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+       u32 reselect            [  4];
+#else
+       u32 reselect            [  2];
+#endif
+       u32 reselected          [ 22];
+       u32 resel_scntl4        [ 20];
+       u32 resel_lun0          [  6];
+#if   SYM_CONF_MAX_TASK*4 > 512
+       u32 resel_tag           [ 26];
+#elif SYM_CONF_MAX_TASK*4 > 256
+       u32 resel_tag           [ 20];
+#else
+       u32 resel_tag           [ 16];
+#endif
+       u32 resel_dsa           [  2];
+       u32 resel_dsa1          [  4];
+       u32 resel_no_tag        [  6];
+       u32 data_in             [SYM_CONF_MAX_SG * 2];
+       u32 data_in2            [  4];
+       u32 data_out            [SYM_CONF_MAX_SG * 2];
+       u32 data_out2           [  4];
+       u32 pm0_data            [ 12];
+       u32 pm0_data_out        [  6];
+       u32 pm0_data_end        [  6];
+       u32 pm1_data            [ 12];
+       u32 pm1_data_out        [  6];
+       u32 pm1_data_end        [  6];
+};
+
+/*
+ *  Script fragments which stay in main memory for all chips 
+ *  except for chips that support 8K on-chip RAM.
+ */
+struct SYM_FWB_SCR {
+       u32 start64             [  2];
+       u32 no_data             [  2];
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+       u32 sel_for_abort       [ 18];
+#else
+       u32 sel_for_abort       [ 16];
+#endif
+       u32 sel_for_abort_1     [  2];
+       u32 msg_in_etc          [ 12];
+       u32 msg_received        [  4];
+       u32 msg_weird_seen      [  4];
+       u32 msg_extended        [ 20];
+       u32 msg_bad             [  6];
+       u32 msg_weird           [  4];
+       u32 msg_weird1          [  8];
+
+       u32 wdtr_resp           [  6];
+       u32 send_wdtr           [  4];
+       u32 sdtr_resp           [  6];
+       u32 send_sdtr           [  4];
+       u32 ppr_resp            [  6];
+       u32 send_ppr            [  4];
+       u32 nego_bad_phase      [  4];
+       u32 msg_out             [  4];
+       u32 msg_out_done        [  4];
+       u32 data_ovrun          [  2];
+       u32 data_ovrun1         [ 22];
+       u32 data_ovrun2         [  8];
+       u32 abort_resel         [ 16];
+       u32 resend_ident        [  4];
+       u32 ident_break         [  4];
+       u32 ident_break_atn     [  4];
+       u32 sdata_in            [  6];
+       u32 resel_bad_lun       [  4];
+       u32 bad_i_t_l           [  4];
+       u32 bad_i_t_l_q         [  4];
+       u32 bad_status          [  6];
+       u32 pm_handle           [ 20];
+       u32 pm_handle1          [  4];
+       u32 pm_save             [  4];
+       u32 pm0_save            [ 12];
+       u32 pm_save_end         [  4];
+       u32 pm1_save            [ 14];
+
+       /* WSR handling */
+       u32 pm_wsr_handle       [ 38];
+       u32 wsr_ma_helper       [  4];
+
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+       /* Unknown direction handling */
+       u32 data_io             [  2];
+       u32 data_io_in          [  2];
+       u32 data_io_com         [  6];
+       u32 data_io_out         [  8];
+#endif
+       /* Data area */
+       u32 zero                [  1];
+       u32 scratch             [  1];
+       u32 pm0_data_addr       [  1];
+       u32 pm1_data_addr       [  1];
+       u32 done_pos            [  1];
+       u32 startpos            [  1];
+       u32 targtbl             [  1];
+};
+
+/*
+ *  Script fragments used at initialisations.
+ *  Only runs out of main memory.
+ */
+struct SYM_FWZ_SCR {
+       u32 snooptest           [  6];
+       u32 snoopend            [  2];
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+       u32 start_ram           [  1];
+       u32 scripta0_ba         [  4];
+       u32 start_ram64         [  3];
+       u32 scripta0_ba64       [  3];
+       u32 scriptb0_ba64       [  6];
+       u32 ram_seg64           [  1];
+#endif
+};
+
+static struct SYM_FWA_SCR SYM_FWA_SCR = {
+/*--------------------------< START >----------------------------*/ {
+       /*
+        *  Switch the LED on.
+        *  Will be patched with a NO_OP if LED
+        *  not needed or not desired.
+        */
+       SCR_REG_REG (gpreg, SCR_AND, 0xfe),
+               0,
+       /*
+        *      Clear SIGP.
+        */
+       SCR_FROM_REG (ctest2),
+               0,
+       /*
+        *  Stop here if the C code wants to perform 
+        *  some error recovery procedure manually.
+        *  (Indicate this by setting SEM in ISTAT)
+        */
+       SCR_FROM_REG (istat),
+               0,
+       /*
+        *  Report to the C code the next position in 
+        *  the start queue the SCRIPTS will schedule.
+        *  The C code must not change SCRATCHA.
+        */
+       SCR_LOAD_ABS (scratcha, 4),
+               PADDR_B (startpos),
+       SCR_INT ^ IFTRUE (MASK (SEM, SEM)),
+               SIR_SCRIPT_STOPPED,
+       /*
+        *  Start the next job.
+        *
+        *  @DSA     = start point for this job.
+        *  SCRATCHA = address of this job in the start queue.
+        *
+        *  We will restore startpos with SCRATCHA if we fails the 
+        *  arbitration or if it is the idle job.
+        *
+        *  The below GETJOB_BEGIN to GETJOB_END section of SCRIPTS 
+        *  is a critical path. If it is partially executed, it then 
+        *  may happen that the job address is not yet in the DSA 
+        *  and the next queue position points to the next JOB.
+        */
+       SCR_LOAD_ABS (dsa, 4),
+               PADDR_B (startpos),
+       SCR_LOAD_REL (temp, 4),
+               4,
+}/*-------------------------< GETJOB_BEGIN >---------------------*/,{
+       SCR_STORE_ABS (temp, 4),
+               PADDR_B (startpos),
+       SCR_LOAD_REL (dsa, 4),
+               0,
+}/*-------------------------< GETJOB_END >-----------------------*/,{
+       SCR_LOAD_REL (temp, 4),
+               0,
+       SCR_RETURN,
+               0,
+}/*-------------------------< SELECT >---------------------------*/,{
+       /*
+        *  DSA contains the address of a scheduled
+        *      data structure.
+        *
+        *  SCRATCHA contains the address of the start queue  
+        *      entry which points to the next job.
+        *
+        *  Set Initiator mode.
+        *
+        *  (Target mode is left as an exercise for the reader)
+        */
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+       SCR_CLR (SCR_TRG),
+               0,
+#endif
+       /*
+        *      And try to select this target.
+        */
+       SCR_SEL_TBL_ATN ^ offsetof (struct sym_dsb, select),
+               PADDR_A (ungetjob),
+       /*
+        *  Now there are 4 possibilities:
+        *
+        *  (1) The chip looses arbitration.
+        *  This is ok, because it will try again,
+        *  when the bus becomes idle.
+        *  (But beware of the timeout function!)
+        *
+        *  (2) The chip is reselected.
+        *  Then the script processor takes the jump
+        *  to the RESELECT label.
+        *
+        *  (3) The chip wins arbitration.
+        *  Then it will execute SCRIPTS instruction until 
+        *  the next instruction that checks SCSI phase.
+        *  Then will stop and wait for selection to be 
+        *  complete or selection time-out to occur.
+        *
+        *  After having won arbitration, the SCRIPTS  
+        *  processor is able to execute instructions while 
+        *  the SCSI core is performing SCSI selection.
+        */
+       /*
+        *      Initialize the status registers
+        */
+       SCR_LOAD_REL (scr0, 4),
+               offsetof (struct sym_ccb, phys.head.status),
+       /*
+        *  We may need help from CPU if the DMA segment 
+        *  registers aren't up-to-date for this IO.
+        *  Patched with NOOP for chips that donnot 
+        *  support DAC addressing.
+        */
+#if    SYM_CONF_DMA_ADDRESSING_MODE == 2
+}/*-------------------------< IS_DMAP_DIRTY >--------------------*/,{
+       SCR_FROM_REG (HX_REG),
+               0,
+       SCR_INT ^ IFTRUE (MASK (HX_DMAP_DIRTY, HX_DMAP_DIRTY)),
+               SIR_DMAP_DIRTY,
+#endif
+}/*-------------------------< WF_SEL_DONE >----------------------*/,{
+       SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+               SIR_SEL_ATN_NO_MSG_OUT,
+}/*-------------------------< SEL_DONE >-------------------------*/,{
+       /*
+        *  C1010-33 errata work-around.
+        *  Due to a race, the SCSI core may not have 
+        *  loaded SCNTL3 on SEL_TBL instruction.
+        *  We reload it once phase is stable.
+        *  Patched with a NOOP for other chips.
+        */
+       SCR_LOAD_REL (scntl3, 1),
+               offsetof(struct sym_dsb, select.sel_scntl3),
+}/*-------------------------< SEND_IDENT >-----------------------*/,{
+       /*
+        *  Selection complete.
+        *  Send the IDENTIFY and possibly the TAG message 
+        *  and negotiation message if present.
+        */
+       SCR_MOVE_TBL ^ SCR_MSG_OUT,
+               offsetof (struct sym_dsb, smsg),
+}/*-------------------------< SELECT2 >--------------------------*/,{
+#ifdef SYM_CONF_IARB_SUPPORT
+       /*
+        *  Set IMMEDIATE ARBITRATION if we have been given 
+        *  a hint to do so. (Some job to do after this one).
+        */
+       SCR_FROM_REG (HF_REG),
+               0,
+       SCR_JUMPR ^ IFFALSE (MASK (HF_HINT_IARB, HF_HINT_IARB)),
+               8,
+       SCR_REG_REG (scntl1, SCR_OR, IARB),
+               0,
+#endif
+       /*
+        *  Anticipate the COMMAND phase.
+        *  This is the PHASE we expect at this point.
+        */
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_COMMAND)),
+               PADDR_A (sel_no_cmd),
+}/*-------------------------< COMMAND >--------------------------*/,{
+       /*
+        *  ... and send the command
+        */
+       SCR_MOVE_TBL ^ SCR_COMMAND,
+               offsetof (struct sym_dsb, cmd),
+}/*-------------------------< DISPATCH >-------------------------*/,{
+       /*
+        *  MSG_IN is the only phase that shall be 
+        *  entered at least once for each (re)selection.
+        *  So we test it first.
+        */
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+               PADDR_A (msg_in),
+       SCR_JUMP ^ IFTRUE (IF (SCR_DATA_OUT)),
+               PADDR_A (datao_phase),
+       SCR_JUMP ^ IFTRUE (IF (SCR_DATA_IN)),
+               PADDR_A (datai_phase),
+       SCR_JUMP ^ IFTRUE (IF (SCR_STATUS)),
+               PADDR_A (status),
+       SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND)),
+               PADDR_A (command),
+       SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)),
+               PADDR_B (msg_out),
+       /*
+        *  Discard as many illegal phases as 
+        *  required and tell the C code about.
+        */
+       SCR_JUMPR ^ IFFALSE (WHEN (SCR_ILG_OUT)),
+               16,
+       SCR_MOVE_ABS (1) ^ SCR_ILG_OUT,
+               HADDR_1 (scratch),
+       SCR_JUMPR ^ IFTRUE (WHEN (SCR_ILG_OUT)),
+               -16,
+       SCR_JUMPR ^ IFFALSE (WHEN (SCR_ILG_IN)),
+               16,
+       SCR_MOVE_ABS (1) ^ SCR_ILG_IN,
+               HADDR_1 (scratch),
+       SCR_JUMPR ^ IFTRUE (WHEN (SCR_ILG_IN)),
+               -16,
+       SCR_INT,
+               SIR_BAD_PHASE,
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< SEL_NO_CMD >-----------------------*/,{
+       /*
+        *  The target does not switch to command 
+        *  phase after IDENTIFY has been sent.
+        *
+        *  If it stays in MSG OUT phase send it 
+        *  the IDENTIFY again.
+        */
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+               PADDR_B (resend_ident),
+       /*
+        *  If target does not switch to MSG IN phase 
+        *  and we sent a negotiation, assert the 
+        *  failure immediately.
+        */
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+               PADDR_A (dispatch),
+       SCR_FROM_REG (HS_REG),
+               0,
+       SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
+               SIR_NEGO_FAILED,
+       /*
+        *  Jump to dispatcher.
+        */
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< INIT >-----------------------------*/,{
+       /*
+        *  Wait for the SCSI RESET signal to be 
+        *  inactive before restarting operations, 
+        *  since the chip may hang on SEL_ATN 
+        *  if SCSI RESET is active.
+        */
+       SCR_FROM_REG (sstat0),
+               0,
+       SCR_JUMPR ^ IFTRUE (MASK (IRST, IRST)),
+               -16,
+       SCR_JUMP,
+               PADDR_A (start),
+}/*-------------------------< CLRACK >---------------------------*/,{
+       /*
+        *  Terminate possible pending message phase.
+        */
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< DATAI_DONE >-----------------------*/,{
+       /*
+        *  Save current pointer to LASTP.
+        */
+       SCR_STORE_REL (temp, 4),
+               offsetof (struct sym_ccb, phys.head.lastp),
+       /*
+        *  If the SWIDE is not full, jump to dispatcher.
+        *  We anticipate a STATUS phase.
+        */
+       SCR_FROM_REG (scntl2),
+               0,
+       SCR_JUMP ^ IFTRUE (MASK (WSR, WSR)),
+               PADDR_A (datai_done_wsr),
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_STATUS)),
+               PADDR_A (status),
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< DATAI_DONE_WSR >-------------------*/,{
+       /*
+        *  The SWIDE is full.
+        *  Clear this condition.
+        */
+       SCR_REG_REG (scntl2, SCR_OR, WSR),
+               0,
+       /*
+        *  We are expecting an IGNORE RESIDUE message 
+        *  from the device, otherwise we are in data 
+        *  overrun condition. Check against MSG_IN phase.
+        */
+       SCR_INT ^ IFFALSE (WHEN (SCR_MSG_IN)),
+               SIR_SWIDE_OVERRUN,
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+               PADDR_A (dispatch),
+       /*
+        *  We are in MSG_IN phase,
+        *  Read the first byte of the message.
+        *  If it is not an IGNORE RESIDUE message,
+        *  signal overrun and jump to message 
+        *  processing.
+        */
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               HADDR_1 (msgin[0]),
+       SCR_INT ^ IFFALSE (DATA (M_IGN_RESIDUE)),
+               SIR_SWIDE_OVERRUN,
+       SCR_JUMP ^ IFFALSE (DATA (M_IGN_RESIDUE)),
+               PADDR_A (msg_in2),
+       /*
+        *  We got the message we expected.
+        *  Read the 2nd byte, and jump to dispatcher.
+        */
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               HADDR_1 (msgin[1]),
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< DATAO_DONE >-----------------------*/,{
+       /*
+        *  Save current pointer to LASTP.
+        */
+       SCR_STORE_REL (temp, 4),
+               offsetof (struct sym_ccb, phys.head.lastp),
+       /*
+        *  If the SODL is not full jump to dispatcher.
+        *  We anticipate a STATUS phase.
+        */
+       SCR_FROM_REG (scntl2),
+               0,
+       SCR_JUMP ^ IFTRUE (MASK (WSS, WSS)),
+               PADDR_A (datao_done_wss),
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_STATUS)),
+               PADDR_A (status),
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< DATAO_DONE_WSS >-------------------*/,{
+       /*
+        *  The SODL is full, clear this condition.
+        */
+       SCR_REG_REG (scntl2, SCR_OR, WSS),
+               0,
+       /*
+        *  And signal a DATA UNDERRUN condition 
+        *  to the C code.
+        */
+       SCR_INT,
+               SIR_SODL_UNDERRUN,
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< DATAI_PHASE >----------------------*/,{
+       /*
+        *  Jump to current pointer.
+        */
+       SCR_LOAD_REL (temp, 4),
+               offsetof (struct sym_ccb, phys.head.lastp),
+       SCR_RETURN,
+               0,
+}/*-------------------------< DATAO_PHASE >----------------------*/,{
+       /*
+        *  C1010-66 errata work-around.
+        *  Extra clocks of data hold must be inserted 
+        *  in DATA OUT phase on 33 MHz PCI BUS.
+        *  Patched with a NOOP for other chips.
+        */
+       SCR_REG_REG (scntl4, SCR_OR, (XCLKH_DT|XCLKH_ST)),
+               0,
+       /*
+        *  Jump to current pointer.
+        */
+       SCR_LOAD_REL (temp, 4),
+               offsetof (struct sym_ccb, phys.head.lastp),
+       SCR_RETURN,
+               0,
+}/*-------------------------< MSG_IN >---------------------------*/,{
+       /*
+        *  Get the first byte of the message.
+        *
+        *  The script processor doesn't negate the
+        *  ACK signal after this transfer.
+        */
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               HADDR_1 (msgin[0]),
+}/*-------------------------< MSG_IN2 >--------------------------*/,{
+       /*
+        *  Check first against 1 byte messages 
+        *  that we handle from SCRIPTS.
+        */
+       SCR_JUMP ^ IFTRUE (DATA (M_COMPLETE)),
+               PADDR_A (complete),
+       SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)),
+               PADDR_A (disconnect),
+       SCR_JUMP ^ IFTRUE (DATA (M_SAVE_DP)),
+               PADDR_A (save_dp),
+       SCR_JUMP ^ IFTRUE (DATA (M_RESTORE_DP)),
+               PADDR_A (restore_dp),
+       /*
+        *  We handle all other messages from the 
+        *  C code, so no need to waste on-chip RAM 
+        *  for those ones.
+        */
+       SCR_JUMP,
+               PADDR_B (msg_in_etc),
+}/*-------------------------< STATUS >---------------------------*/,{
+       /*
+        *  get the status
+        */
+       SCR_MOVE_ABS (1) ^ SCR_STATUS,
+               HADDR_1 (scratch),
+#ifdef SYM_CONF_IARB_SUPPORT
+       /*
+        *  If STATUS is not GOOD, clear IMMEDIATE ARBITRATION, 
+        *  since we may have to tamper the start queue from 
+        *  the C code.
+        */
+       SCR_JUMPR ^ IFTRUE (DATA (S_GOOD)),
+               8,
+       SCR_REG_REG (scntl1, SCR_AND, ~IARB),
+               0,
+#endif
+       /*
+        *  save status to scsi_status.
+        *  mark as complete.
+        */
+       SCR_TO_REG (SS_REG),
+               0,
+       SCR_LOAD_REG (HS_REG, HS_COMPLETE),
+               0,
+       /*
+        *  Anticipate the MESSAGE PHASE for 
+        *  the TASK COMPLETE message.
+        */
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+               PADDR_A (msg_in),
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< COMPLETE >-------------------------*/,{
+       /*
+        *  Complete message.
+        *
+        *  When we terminate the cycle by clearing ACK,
+        *  the target may disconnect immediately.
+        *
+        *  We don't want to be told of an "unexpected disconnect",
+        *  so we disable this feature.
+        */
+       SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+               0,
+       /*
+        *  Terminate cycle ...
+        */
+       SCR_CLR (SCR_ACK|SCR_ATN),
+               0,
+       /*
+        *  ... and wait for the disconnect.
+        */
+       SCR_WAIT_DISC,
+               0,
+}/*-------------------------< COMPLETE2 >------------------------*/,{
+       /*
+        *  Save host status.
+        */
+       SCR_STORE_REL (scr0, 4),
+               offsetof (struct sym_ccb, phys.head.status),
+       /*
+        *  Some bridges may reorder DMA writes to memory.
+        *  We donnot want the CPU to deal with completions  
+        *  without all the posted write having been flushed 
+        *  to memory. This DUMMY READ should flush posted 
+        *  buffers prior to the CPU having to deal with 
+        *  completions.
+        */
+       SCR_LOAD_REL (scr0, 4), /* DUMMY READ */
+               offsetof (struct sym_ccb, phys.head.status),
+
+       /*
+        *  If command resulted in not GOOD status,
+        *  call the C code if needed.
+        */
+       SCR_FROM_REG (SS_REG),
+               0,
+       SCR_CALL ^ IFFALSE (DATA (S_GOOD)),
+               PADDR_B (bad_status),
+       /*
+        *  If we performed an auto-sense, call 
+        *  the C code to synchronyze task aborts 
+        *  with UNIT ATTENTION conditions.
+        */
+       SCR_FROM_REG (HF_REG),
+               0,
+       SCR_JUMP ^ IFFALSE (MASK (0 ,(HF_SENSE|HF_EXT_ERR))),
+               PADDR_A (complete_error),
+}/*-------------------------< DONE >-----------------------------*/,{
+       /*
+        *  Copy the DSA to the DONE QUEUE and 
+        *  signal completion to the host.
+        *  If we are interrupted between DONE 
+        *  and DONE_END, we must reset, otherwise 
+        *  the completed CCB may be lost.
+        */
+       SCR_STORE_ABS (dsa, 4),
+               PADDR_B (scratch),
+       SCR_LOAD_ABS (dsa, 4),
+               PADDR_B (done_pos),
+       SCR_LOAD_ABS (scratcha, 4),
+               PADDR_B (scratch),
+       SCR_STORE_REL (scratcha, 4),
+               0,
+       /*
+        *  The instruction below reads the DONE QUEUE next 
+        *  free position from memory.
+        *  In addition it ensures that all PCI posted writes  
+        *  are flushed and so the DSA value of the done 
+        *  CCB is visible by the CPU before INTFLY is raised.
+        */
+       SCR_LOAD_REL (scratcha, 4),
+               4,
+       SCR_INT_FLY,
+               0,
+       SCR_STORE_ABS (scratcha, 4),
+               PADDR_B (done_pos),
+}/*-------------------------< DONE_END >-------------------------*/,{
+       SCR_JUMP,
+               PADDR_A (start),
+}/*-------------------------< COMPLETE_ERROR >-------------------*/,{
+       SCR_LOAD_ABS (scratcha, 4),
+               PADDR_B (startpos),
+       SCR_INT,
+               SIR_COMPLETE_ERROR,
+}/*-------------------------< SAVE_DP >--------------------------*/,{
+       /*
+        *  Clear ACK immediately.
+        *  No need to delay it.
+        */
+       SCR_CLR (SCR_ACK),
+               0,
+       /*
+        *  Keep track we received a SAVE DP, so 
+        *  we will switch to the other PM context 
+        *  on the next PM since the DP may point 
+        *  to the current PM context.
+        */
+       SCR_REG_REG (HF_REG, SCR_OR, HF_DP_SAVED),
+               0,
+       /*
+        *  SAVE_DP message:
+        *  Copy LASTP to SAVEP.
+        */
+       SCR_LOAD_REL (scratcha, 4),
+               offsetof (struct sym_ccb, phys.head.lastp),
+       SCR_STORE_REL (scratcha, 4),
+               offsetof (struct sym_ccb, phys.head.savep),
+       /*
+        *  Anticipate the MESSAGE PHASE for 
+        *  the DISCONNECT message.
+        */
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)),
+               PADDR_A (msg_in),
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< RESTORE_DP >-----------------------*/,{
+       /*
+        *  Clear ACK immediately.
+        *  No need to delay it.
+        */
+       SCR_CLR (SCR_ACK),
+               0,
+       /*
+        *  Copy SAVEP to LASTP.
+        */
+       SCR_LOAD_REL  (scratcha, 4),
+               offsetof (struct sym_ccb, phys.head.savep),
+       SCR_STORE_REL (scratcha, 4),
+               offsetof (struct sym_ccb, phys.head.lastp),
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< DISCONNECT >-----------------------*/,{
+       /*
+        *  DISCONNECTing  ...
+        *
+        *  disable the "unexpected disconnect" feature,
+        *  and remove the ACK signal.
+        */
+       SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+               0,
+       SCR_CLR (SCR_ACK|SCR_ATN),
+               0,
+       /*
+        *  Wait for the disconnect.
+        */
+       SCR_WAIT_DISC,
+               0,
+       /*
+        *  Status is: DISCONNECTED.
+        */
+       SCR_LOAD_REG (HS_REG, HS_DISCONNECT),
+               0,
+       /*
+        *  Save host status.
+        */
+       SCR_STORE_REL (scr0, 4),
+               offsetof (struct sym_ccb, phys.head.status),
+       SCR_JUMP,
+               PADDR_A (start),
+}/*-------------------------< IDLE >-----------------------------*/,{
+       /*
+        *  Nothing to do?
+        *  Switch the LED off and wait for reselect.
+        *  Will be patched with a NO_OP if LED
+        *  not needed or not desired.
+        */
+       SCR_REG_REG (gpreg, SCR_OR, 0x01),
+               0,
+#ifdef SYM_CONF_IARB_SUPPORT
+       SCR_JUMPR,
+               8,
+#endif
+}/*-------------------------< UNGETJOB >-------------------------*/,{
+#ifdef SYM_CONF_IARB_SUPPORT
+       /*
+        *  Set IMMEDIATE ARBITRATION, for the next time.
+        *  This will give us better chance to win arbitration 
+        *  for the job we just wanted to do.
+        */
+       SCR_REG_REG (scntl1, SCR_OR, IARB),
+               0,
+#endif
+       /*
+        *  We are not able to restart the SCRIPTS if we are 
+        *  interrupted and these instruction haven't been 
+        *  all executed. BTW, this is very unlikely to 
+        *  happen, but we check that from the C code.
+        */
+       SCR_LOAD_REG (dsa, 0xff),
+               0,
+       SCR_STORE_ABS (scratcha, 4),
+               PADDR_B (startpos),
+}/*-------------------------< RESELECT >-------------------------*/,{
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+       /*
+        *  Make sure we are in initiator mode.
+        */
+       SCR_CLR (SCR_TRG),
+               0,
+#endif
+       /*
+        *  Sleep waiting for a reselection.
+        */
+       SCR_WAIT_RESEL,
+               PADDR_A(start),
+}/*-------------------------< RESELECTED >-----------------------*/,{
+       /*
+        *  Switch the LED on.
+        *  Will be patched with a NO_OP if LED
+        *  not needed or not desired.
+        */
+       SCR_REG_REG (gpreg, SCR_AND, 0xfe),
+               0,
+       /*
+        *  load the target id into the sdid
+        */
+       SCR_REG_SFBR (ssid, SCR_AND, 0x8F),
+               0,
+       SCR_TO_REG (sdid),
+               0,
+       /*
+        *  Load the target control block address
+        */
+       SCR_LOAD_ABS (dsa, 4),
+               PADDR_B (targtbl),
+       SCR_SFBR_REG (dsa, SCR_SHL, 0),
+               0,
+       SCR_REG_REG (dsa, SCR_SHL, 0),
+               0,
+       SCR_REG_REG (dsa, SCR_AND, 0x3c),
+               0,
+       SCR_LOAD_REL (dsa, 4),
+               0,
+       /*
+        *  We expect MESSAGE IN phase.
+        *  If not, get help from the C code.
+        */
+       SCR_INT ^ IFFALSE (WHEN (SCR_MSG_IN)),
+               SIR_RESEL_NO_MSG_IN,
+       /*
+        *  Load the legacy synchronous transfer registers.
+        */
+       SCR_LOAD_REL (scntl3, 1),
+               offsetof(struct sym_tcb, head.wval),
+       SCR_LOAD_REL (sxfer, 1),
+               offsetof(struct sym_tcb, head.sval),
+}/*-------------------------< RESEL_SCNTL4 >---------------------*/,{
+       /*
+        *  The C1010 uses a new synchronous timing scheme.
+        *  Will be patched with a NO_OP if not a C1010.
+        */
+       SCR_LOAD_REL (scntl4, 1),
+               offsetof(struct sym_tcb, head.uval),
+       /*
+        *  Get the IDENTIFY message.
+        */
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               HADDR_1 (msgin),
+       /*
+        *  If IDENTIFY LUN #0, use a faster path 
+        *  to find the LCB structure.
+        */
+       SCR_JUMP ^ IFTRUE (MASK (0x80, 0xbf)),
+               PADDR_A (resel_lun0),
+       /*
+        *  If message isn't an IDENTIFY, 
+        *  tell the C code about.
+        */
+       SCR_INT ^ IFFALSE (MASK (0x80, 0x80)),
+               SIR_RESEL_NO_IDENTIFY,
+       /*
+        *  It is an IDENTIFY message,
+        *  Load the LUN control block address.
+        */
+       SCR_LOAD_REL (dsa, 4),
+               offsetof(struct sym_tcb, head.luntbl_sa),
+       SCR_SFBR_REG (dsa, SCR_SHL, 0),
+               0,
+       SCR_REG_REG (dsa, SCR_SHL, 0),
+               0,
+       SCR_REG_REG (dsa, SCR_AND, 0xfc),
+               0,
+       SCR_LOAD_REL (dsa, 4),
+               0,
+       SCR_JUMPR,
+               8,
+}/*-------------------------< RESEL_LUN0 >-----------------------*/,{
+       /*
+        *  LUN 0 special case (but usual one :))
+        */
+       SCR_LOAD_REL (dsa, 4),
+               offsetof(struct sym_tcb, head.lun0_sa),
+       /*
+        *  Jump indirectly to the reselect action for this LUN.
+        */
+       SCR_LOAD_REL (temp, 4),
+               offsetof(struct sym_lcb, head.resel_sa),
+       SCR_RETURN,
+               0,
+       /* In normal situations, we jump to RESEL_TAG or RESEL_NO_TAG */
+}/*-------------------------< RESEL_TAG >------------------------*/,{
+       /*
+        *  ACK the IDENTIFY previously received.
+        */
+       SCR_CLR (SCR_ACK),
+               0,
+       /*
+        *  It shall be a tagged command.
+        *  Read SIMPLE+TAG.
+        *  The C code will deal with errors.
+        *  Agressive optimization, is'nt it? :)
+        */
+       SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
+               HADDR_1 (msgin),
+       /*
+        *  Load the pointer to the tagged task 
+        *  table for this LUN.
+        */
+       SCR_LOAD_REL (dsa, 4),
+               offsetof(struct sym_lcb, head.itlq_tbl_sa),
+       /*
+        *  The SIDL still contains the TAG value.
+        *  Agressive optimization, isn't it? :):)
+        */
+       SCR_REG_SFBR (sidl, SCR_SHL, 0),
+               0,
+#if SYM_CONF_MAX_TASK*4 > 512
+       SCR_JUMPR ^ IFFALSE (CARRYSET),
+               8,
+       SCR_REG_REG (dsa1, SCR_OR, 2),
+               0,
+       SCR_REG_REG (sfbr, SCR_SHL, 0),
+               0,
+       SCR_JUMPR ^ IFFALSE (CARRYSET),
+               8,
+       SCR_REG_REG (dsa1, SCR_OR, 1),
+               0,
+#elif SYM_CONF_MAX_TASK*4 > 256
+       SCR_JUMPR ^ IFFALSE (CARRYSET),
+               8,
+       SCR_REG_REG (dsa1, SCR_OR, 1),
+               0,
+#endif
+       /*
+        *  Retrieve the DSA of this task.
+        *  JUMP indirectly to the restart point of the CCB.
+        */
+       SCR_SFBR_REG (dsa, SCR_AND, 0xfc),
+               0,
+       SCR_LOAD_REL (dsa, 4),
+               0,
+       SCR_LOAD_REL (temp, 4),
+               offsetof(struct sym_ccb, phys.head.go.restart),
+       SCR_RETURN,
+               0,
+       /* In normal situations we branch to RESEL_DSA */
+}/*-------------------------< RESEL_DSA >------------------------*/,{
+       /*
+        *  ACK the IDENTIFY or TAG previously received.
+        */
+       SCR_CLR (SCR_ACK),
+               0,
+}/*-------------------------< RESEL_DSA1 >-----------------------*/,{
+       /*
+        *      Initialize the status registers
+        */
+       SCR_LOAD_REL (scr0, 4),
+               offsetof (struct sym_ccb, phys.head.status),
+       /*
+        *  Jump to dispatcher.
+        */
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< RESEL_NO_TAG >---------------------*/,{
+       /*
+        *  Load the DSA with the unique ITL task.
+        */
+       SCR_LOAD_REL (dsa, 4),
+               offsetof(struct sym_lcb, head.itl_task_sa),
+       /*
+        *  JUMP indirectly to the restart point of the CCB.
+        */
+       SCR_LOAD_REL (temp, 4),
+               offsetof(struct sym_ccb, phys.head.go.restart),
+       SCR_RETURN,
+               0,
+       /* In normal situations we branch to RESEL_DSA */
+}/*-------------------------< DATA_IN >--------------------------*/,{
+/*
+ *  Because the size depends on the
+ *  #define SYM_CONF_MAX_SG parameter,
+ *  it is filled in at runtime.
+ *
+ *  ##===========< i=0; i<SYM_CONF_MAX_SG >=========
+ *  || SCR_CHMOV_TBL ^ SCR_DATA_IN,
+ *  ||         offsetof (struct sym_dsb, data[ i]),
+ *  ##==========================================
+ */
+0
+}/*-------------------------< DATA_IN2 >-------------------------*/,{
+       SCR_CALL,
+               PADDR_A (datai_done),
+       SCR_JUMP,
+               PADDR_B (data_ovrun),
+}/*-------------------------< DATA_OUT >-------------------------*/,{
+/*
+ *  Because the size depends on the
+ *  #define SYM_CONF_MAX_SG parameter,
+ *  it is filled in at runtime.
+ *
+ *  ##===========< i=0; i<SYM_CONF_MAX_SG >=========
+ *  || SCR_CHMOV_TBL ^ SCR_DATA_OUT,
+ *  ||         offsetof (struct sym_dsb, data[ i]),
+ *  ##==========================================
+ */
+0
+}/*-------------------------< DATA_OUT2 >------------------------*/,{
+       SCR_CALL,
+               PADDR_A (datao_done),
+       SCR_JUMP,
+               PADDR_B (data_ovrun),
+}/*-------------------------< PM0_DATA >-------------------------*/,{
+       /*
+        *  Read our host flags to SFBR, so we will be able 
+        *  to check against the data direction we expect.
+        */
+       SCR_FROM_REG (HF_REG),
+               0,
+       /*
+        *  Check against actual DATA PHASE.
+        */
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+               PADDR_A (pm0_data_out),
+       /*
+        *  Actual phase is DATA IN.
+        *  Check against expected direction.
+        */
+       SCR_JUMP ^ IFFALSE (MASK (HF_DATA_IN, HF_DATA_IN)),
+               PADDR_B (data_ovrun),
+       /*
+        *  Keep track we are moving data from the 
+        *  PM0 DATA mini-script.
+        */
+       SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM0),
+               0,
+       /*
+        *  Move the data to memory.
+        */
+       SCR_CHMOV_TBL ^ SCR_DATA_IN,
+               offsetof (struct sym_ccb, phys.pm0.sg),
+       SCR_JUMP,
+               PADDR_A (pm0_data_end),
+}/*-------------------------< PM0_DATA_OUT >---------------------*/,{
+       /*
+        *  Actual phase is DATA OUT.
+        *  Check against expected direction.
+        */
+       SCR_JUMP ^ IFTRUE (MASK (HF_DATA_IN, HF_DATA_IN)),
+               PADDR_B (data_ovrun),
+       /*
+        *  Keep track we are moving data from the 
+        *  PM0 DATA mini-script.
+        */
+       SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM0),
+               0,
+       /*
+        *  Move the data from memory.
+        */
+       SCR_CHMOV_TBL ^ SCR_DATA_OUT,
+               offsetof (struct sym_ccb, phys.pm0.sg),
+}/*-------------------------< PM0_DATA_END >---------------------*/,{
+       /*
+        *  Clear the flag that told we were moving  
+        *  data from the PM0 DATA mini-script.
+        */
+       SCR_REG_REG (HF_REG, SCR_AND, (~HF_IN_PM0)),
+               0,
+       /*
+        *  Return to the previous DATA script which 
+        *  is guaranteed by design (if no bug) to be 
+        *  the main DATA script for this transfer.
+        */
+       SCR_LOAD_REL (temp, 4),
+               offsetof (struct sym_ccb, phys.pm0.ret),
+       SCR_RETURN,
+               0,
+}/*-------------------------< PM1_DATA >-------------------------*/,{
+       /*
+        *  Read our host flags to SFBR, so we will be able 
+        *  to check against the data direction we expect.
+        */
+       SCR_FROM_REG (HF_REG),
+               0,
+       /*
+        *  Check against actual DATA PHASE.
+        */
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+               PADDR_A (pm1_data_out),
+       /*
+        *  Actual phase is DATA IN.
+        *  Check against expected direction.
+        */
+       SCR_JUMP ^ IFFALSE (MASK (HF_DATA_IN, HF_DATA_IN)),
+               PADDR_B (data_ovrun),
+       /*
+        *  Keep track we are moving data from the 
+        *  PM1 DATA mini-script.
+        */
+       SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM1),
+               0,
+       /*
+        *  Move the data to memory.
+        */
+       SCR_CHMOV_TBL ^ SCR_DATA_IN,
+               offsetof (struct sym_ccb, phys.pm1.sg),
+       SCR_JUMP,
+               PADDR_A (pm1_data_end),
+}/*-------------------------< PM1_DATA_OUT >---------------------*/,{
+       /*
+        *  Actual phase is DATA OUT.
+        *  Check against expected direction.
+        */
+       SCR_JUMP ^ IFTRUE (MASK (HF_DATA_IN, HF_DATA_IN)),
+               PADDR_B (data_ovrun),
+       /*
+        *  Keep track we are moving data from the 
+        *  PM1 DATA mini-script.
+        */
+       SCR_REG_REG (HF_REG, SCR_OR, HF_IN_PM1),
+               0,
+       /*
+        *  Move the data from memory.
+        */
+       SCR_CHMOV_TBL ^ SCR_DATA_OUT,
+               offsetof (struct sym_ccb, phys.pm1.sg),
+}/*-------------------------< PM1_DATA_END >---------------------*/,{
+       /*
+        *  Clear the flag that told we were moving  
+        *  data from the PM1 DATA mini-script.
+        */
+       SCR_REG_REG (HF_REG, SCR_AND, (~HF_IN_PM1)),
+               0,
+       /*
+        *  Return to the previous DATA script which 
+        *  is guaranteed by design (if no bug) to be 
+        *  the main DATA script for this transfer.
+        */
+       SCR_LOAD_REL (temp, 4),
+               offsetof (struct sym_ccb, phys.pm1.ret),
+       SCR_RETURN,
+               0,
+}/*-------------------------<>-----------------------------------*/
+};
+
+static struct SYM_FWB_SCR SYM_FWB_SCR = {
+/*--------------------------< START64 >--------------------------*/ {
+       /*
+        *  SCRIPT entry point for the 895A, 896 and 1010.
+        *  For now, there is no specific stuff for those 
+        *  chips at this point, but this may come.
+        */
+       SCR_JUMP,
+               PADDR_A (init),
+}/*-------------------------< NO_DATA >--------------------------*/,{
+       SCR_JUMP,
+               PADDR_B (data_ovrun),
+}/*-------------------------< SEL_FOR_ABORT >--------------------*/,{
+       /*
+        *  We are jumped here by the C code, if we have 
+        *  some target to reset or some disconnected 
+        *  job to abort. Since error recovery is a serious 
+        *  busyness, we will really reset the SCSI BUS, if 
+        *  case of a SCSI interrupt occuring in this path.
+        */
+#ifdef SYM_CONF_TARGET_ROLE_SUPPORT
+       /*
+        *  Set initiator mode.
+        */
+       SCR_CLR (SCR_TRG),
+               0,
+#endif
+       /*
+        *      And try to select this target.
+        */
+       SCR_SEL_TBL_ATN ^ offsetof (struct sym_hcb, abrt_sel),
+               PADDR_A (reselect),
+       /*
+        *  Wait for the selection to complete or 
+        *  the selection to time out.
+        */
+       SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+               -8,
+       /*
+        *  Call the C code.
+        */
+       SCR_INT,
+               SIR_TARGET_SELECTED,
+       /*
+        *  The C code should let us continue here. 
+        *  Send the 'kiss of death' message.
+        *  We expect an immediate disconnect once 
+        *  the target has eaten the message.
+        */
+       SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+               0,
+       SCR_MOVE_TBL ^ SCR_MSG_OUT,
+               offsetof (struct sym_hcb, abrt_tbl),
+       SCR_CLR (SCR_ACK|SCR_ATN),
+               0,
+       SCR_WAIT_DISC,
+               0,
+       /*
+        *  Tell the C code that we are done.
+        */
+       SCR_INT,
+               SIR_ABORT_SENT,
+}/*-------------------------< SEL_FOR_ABORT_1 >------------------*/,{
+       /*
+        *  Jump at scheduler.
+        */
+       SCR_JUMP,
+               PADDR_A (start),
+}/*-------------------------< MSG_IN_ETC >-----------------------*/,{
+       /*
+        *  If it is an EXTENDED (variable size message)
+        *  Handle it.
+        */
+       SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
+               PADDR_B (msg_extended),
+       /*
+        *  Let the C code handle any other 
+        *  1 byte message.
+        */
+       SCR_JUMP ^ IFTRUE (MASK (0x00, 0xf0)),
+               PADDR_B (msg_received),
+       SCR_JUMP ^ IFTRUE (MASK (0x10, 0xf0)),
+               PADDR_B (msg_received),
+       /*
+        *  We donnot handle 2 bytes messages from SCRIPTS.
+        *  So, let the C code deal with these ones too.
+        */
+       SCR_JUMP ^ IFFALSE (MASK (0x20, 0xf0)),
+               PADDR_B (msg_weird_seen),
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               HADDR_1 (msgin[1]),
+}/*-------------------------< MSG_RECEIVED >---------------------*/,{
+       SCR_LOAD_REL (scratcha, 4),     /* DUMMY READ */
+               0,
+       SCR_INT,
+               SIR_MSG_RECEIVED,
+}/*-------------------------< MSG_WEIRD_SEEN >-------------------*/,{
+       SCR_LOAD_REL (scratcha, 4),     /* DUMMY READ */
+               0,
+       SCR_INT,
+               SIR_MSG_WEIRD,
+}/*-------------------------< MSG_EXTENDED >---------------------*/,{
+       /*
+        *  Clear ACK and get the next byte 
+        *  assumed to be the message length.
+        */
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               HADDR_1 (msgin[1]),
+       /*
+        *  Try to catch some unlikely situations as 0 length 
+        *  or too large the length.
+        */
+       SCR_JUMP ^ IFTRUE (DATA (0)),
+               PADDR_B (msg_weird_seen),
+       SCR_TO_REG (scratcha),
+               0,
+       SCR_REG_REG (sfbr, SCR_ADD, (256-8)),
+               0,
+       SCR_JUMP ^ IFTRUE (CARRYSET),
+               PADDR_B (msg_weird_seen),
+       /*
+        *  We donnot handle extended messages from SCRIPTS.
+        *  Read the amount of data correponding to the 
+        *  message length and call the C code.
+        */
+       SCR_STORE_REL (scratcha, 1),
+               offsetof (struct sym_dsb, smsg_ext.size),
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_MOVE_TBL ^ SCR_MSG_IN,
+               offsetof (struct sym_dsb, smsg_ext),
+       SCR_JUMP,
+               PADDR_B (msg_received),
+}/*-------------------------< MSG_BAD >--------------------------*/,{
+       /*
+        *  unimplemented message - reject it.
+        */
+       SCR_INT,
+               SIR_REJECT_TO_SEND,
+       SCR_SET (SCR_ATN),
+               0,
+       SCR_JUMP,
+               PADDR_A (clrack),
+}/*-------------------------< MSG_WEIRD >------------------------*/,{
+       /*
+        *  weird message received
+        *  ignore all MSG IN phases and reject it.
+        */
+       SCR_INT,
+               SIR_REJECT_TO_SEND,
+       SCR_SET (SCR_ATN),
+               0,
+}/*-------------------------< MSG_WEIRD1 >-----------------------*/,{
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+               PADDR_A (dispatch),
+       SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+               HADDR_1 (scratch),
+       SCR_JUMP,
+               PADDR_B (msg_weird1),
+}/*-------------------------< WDTR_RESP >------------------------*/,{
+       /*
+        *  let the target fetch our answer.
+        */
+       SCR_SET (SCR_ATN),
+               0,
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+               PADDR_B (nego_bad_phase),
+}/*-------------------------< SEND_WDTR >------------------------*/,{
+       /*
+        *  Send the M_X_WIDE_REQ
+        */
+       SCR_MOVE_ABS (4) ^ SCR_MSG_OUT,
+               HADDR_1 (msgout),
+       SCR_JUMP,
+               PADDR_B (msg_out_done),
+}/*-------------------------< SDTR_RESP >------------------------*/,{
+       /*
+        *  let the target fetch our answer.
+        */
+       SCR_SET (SCR_ATN),
+               0,
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+               PADDR_B (nego_bad_phase),
+}/*-------------------------< SEND_SDTR >------------------------*/,{
+       /*
+        *  Send the M_X_SYNC_REQ
+        */
+       SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
+               HADDR_1 (msgout),
+       SCR_JUMP,
+               PADDR_B (msg_out_done),
+}/*-------------------------< PPR_RESP >-------------------------*/,{
+       /*
+        *  let the target fetch our answer.
+        */
+       SCR_SET (SCR_ATN),
+               0,
+       SCR_CLR (SCR_ACK),
+               0,
+       SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+               PADDR_B (nego_bad_phase),
+}/*-------------------------< SEND_PPR >-------------------------*/,{
+       /*
+        *  Send the M_X_PPR_REQ
+        */
+       SCR_MOVE_ABS (8) ^ SCR_MSG_OUT,
+               HADDR_1 (msgout),
+       SCR_JUMP,
+               PADDR_B (msg_out_done),
+}/*-------------------------< NEGO_BAD_PHASE >-------------------*/,{
+       SCR_INT,
+               SIR_NEGO_PROTO,
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< MSG_OUT >--------------------------*/,{
+       /*
+        *  The target requests a message.
+        *  We donnot send messages that may 
+        *  require the device to go to bus free.
+        */
+       SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+               HADDR_1 (msgout),
+       /*
+        *  ... wait for the next phase
+        *  if it's a message out, send it again, ...
+        */
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+               PADDR_B (msg_out),
+}/*-------------------------< MSG_OUT_DONE >---------------------*/,{
+       /*
+        *  Let the C code be aware of the 
+        *  sent message and clear the message.
+        */
+       SCR_INT,
+               SIR_MSG_OUT_DONE,
+       /*
+        *  ... and process the next phase
+        */
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< DATA_OVRUN >-----------------------*/,{
+       /*
+        *  Use scratcha to count the extra bytes.
+        */
+       SCR_LOAD_ABS (scratcha, 4),
+               PADDR_B (zero),
+}/*-------------------------< DATA_OVRUN1 >----------------------*/,{
+       /*
+        *  The target may want to transfer too much data.
+        *
+        *  If phase is DATA OUT write 1 byte and count it.
+        */
+       SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_OUT)),
+               16,
+       SCR_CHMOV_ABS (1) ^ SCR_DATA_OUT,
+               HADDR_1 (scratch),
+       SCR_JUMP,
+               PADDR_B (data_ovrun2),
+       /*
+        *  If WSR is set, clear this condition, and 
+        *  count this byte.
+        */
+       SCR_FROM_REG (scntl2),
+               0,
+       SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)),
+               16,
+       SCR_REG_REG (scntl2, SCR_OR, WSR),
+               0,
+       SCR_JUMP,
+               PADDR_B (data_ovrun2),
+       /*
+        *  Finally check against DATA IN phase.
+        *  Signal data overrun to the C code 
+        *  and jump to dispatcher if not so.
+        *  Read 1 byte otherwise and count it.
+        */
+       SCR_JUMPR ^ IFTRUE (WHEN (SCR_DATA_IN)),
+               16,
+       SCR_INT,
+               SIR_DATA_OVERRUN,
+       SCR_JUMP,
+               PADDR_A (dispatch),
+       SCR_CHMOV_ABS (1) ^ SCR_DATA_IN,
+               HADDR_1 (scratch),
+}/*-------------------------< DATA_OVRUN2 >----------------------*/,{
+       /*
+        *  Count this byte.
+        *  This will allow to return a negative 
+        *  residual to user.
+        */
+       SCR_REG_REG (scratcha,  SCR_ADD,  0x01),
+               0,
+       SCR_REG_REG (scratcha1, SCR_ADDC, 0),
+               0,
+       SCR_REG_REG (scratcha2, SCR_ADDC, 0),
+               0,
+       /*
+        *  .. and repeat as required.
+        */
+       SCR_JUMP,
+               PADDR_B (data_ovrun1),
+}/*-------------------------< ABORT_RESEL >----------------------*/,{
+       SCR_SET (SCR_ATN),
+               0,
+       SCR_CLR (SCR_ACK),
+               0,
+       /*
+        *  send the abort/abortag/reset message
+        *  we expect an immediate disconnect
+        */
+       SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+               0,
+       SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+               HADDR_1 (msgout),
+       SCR_CLR (SCR_ACK|SCR_ATN),
+               0,
+       SCR_WAIT_DISC,
+               0,
+       SCR_INT,
+               SIR_RESEL_ABORTED,
+       SCR_JUMP,
+               PADDR_A (start),
+}/*-------------------------< RESEND_IDENT >---------------------*/,{
+       /*
+        *  The target stays in MSG OUT phase after having acked 
+        *  Identify [+ Tag [+ Extended message ]]. Targets shall
+        *  behave this way on parity error.
+        *  We must send it again all the messages.
+        */
+       SCR_SET (SCR_ATN), /* Shall be asserted 2 deskew delays before the  */
+               0,         /* 1rst ACK = 90 ns. Hope the chip isn't too fast */
+       SCR_JUMP,
+               PADDR_A (send_ident),
+}/*-------------------------< IDENT_BREAK >----------------------*/,{
+       SCR_CLR (SCR_ATN),
+               0,
+       SCR_JUMP,
+               PADDR_A (select2),
+}/*-------------------------< IDENT_BREAK_ATN >------------------*/,{
+       SCR_SET (SCR_ATN),
+               0,
+       SCR_JUMP,
+               PADDR_A (select2),
+}/*-------------------------< SDATA_IN >-------------------------*/,{
+       SCR_CHMOV_TBL ^ SCR_DATA_IN,
+               offsetof (struct sym_dsb, sense),
+       SCR_CALL,
+               PADDR_A (datai_done),
+       SCR_JUMP,
+               PADDR_B (data_ovrun),
+}/*-------------------------< RESEL_BAD_LUN >--------------------*/,{
+       /*
+        *  Message is an IDENTIFY, but lun is unknown.
+        *  Signal problem to C code for logging the event.
+        *  Send a M_ABORT to clear all pending tasks.
+        */
+       SCR_INT,
+               SIR_RESEL_BAD_LUN,
+       SCR_JUMP,
+               PADDR_B (abort_resel),
+}/*-------------------------< BAD_I_T_L >------------------------*/,{
+       /*
+        *  We donnot have a task for that I_T_L.
+        *  Signal problem to C code for logging the event.
+        *  Send a M_ABORT message.
+        */
+       SCR_INT,
+               SIR_RESEL_BAD_I_T_L,
+       SCR_JUMP,
+               PADDR_B (abort_resel),
+}/*-------------------------< BAD_I_T_L_Q >----------------------*/,{
+       /*
+        *  We donnot have a task that matches the tag.
+        *  Signal problem to C code for logging the event.
+        *  Send a M_ABORTTAG message.
+        */
+       SCR_INT,
+               SIR_RESEL_BAD_I_T_L_Q,
+       SCR_JUMP,
+               PADDR_B (abort_resel),
+}/*-------------------------< BAD_STATUS >-----------------------*/,{
+       /*
+        *  Anything different from INTERMEDIATE 
+        *  CONDITION MET should be a bad SCSI status, 
+        *  given that GOOD status has already been tested.
+        *  Call the C code.
+        */
+       SCR_LOAD_ABS (scratcha, 4),
+               PADDR_B (startpos),
+       SCR_INT ^ IFFALSE (DATA (S_COND_MET)),
+               SIR_BAD_SCSI_STATUS,
+       SCR_RETURN,
+               0,
+}/*-------------------------< PM_HANDLE >------------------------*/,{
+       /*
+        *  Phase mismatch handling.
+        *
+        *  Since we have to deal with 2 SCSI data pointers  
+        *  (current and saved), we need at least 2 contexts.
+        *  Each context (pm0 and pm1) has a saved area, a 
+        *  SAVE mini-script and a DATA phase mini-script.
+        */
+       /*
+        *  Get the PM handling flags.
+        */
+       SCR_FROM_REG (HF_REG),
+               0,
+       /*
+        *  If no flags (1rst PM for example), avoid 
+        *  all the below heavy flags testing.
+        *  This makes the normal case a bit faster.
+        */
+       SCR_JUMP ^ IFTRUE (MASK (0, (HF_IN_PM0 | HF_IN_PM1 | HF_DP_SAVED))),
+               PADDR_B (pm_handle1),
+       /*
+        *  If we received a SAVE DP, switch to the 
+        *  other PM context since the savep may point 
+        *  to the current PM context.
+        */
+       SCR_JUMPR ^ IFFALSE (MASK (HF_DP_SAVED, HF_DP_SAVED)),
+               8,
+       SCR_REG_REG (sfbr, SCR_XOR, HF_ACT_PM),
+               0,
+       /*
+        *  If we have been interrupt in a PM DATA mini-script,
+        *  we take the return address from the corresponding 
+        *  saved area.
+        *  This ensure the return address always points to the 
+        *  main DATA script for this transfer.
+        */
+       SCR_JUMP ^ IFTRUE (MASK (0, (HF_IN_PM0 | HF_IN_PM1))),
+               PADDR_B (pm_handle1),
+       SCR_JUMPR ^ IFFALSE (MASK (HF_IN_PM0, HF_IN_PM0)),
+               16,
+       SCR_LOAD_REL (ia, 4),
+               offsetof(struct sym_ccb, phys.pm0.ret),
+       SCR_JUMP,
+               PADDR_B (pm_save),
+       SCR_LOAD_REL (ia, 4),
+               offsetof(struct sym_ccb, phys.pm1.ret),
+       SCR_JUMP,
+               PADDR_B (pm_save),
+}/*-------------------------< PM_HANDLE1 >-----------------------*/,{
+       /*
+        *  Normal case.
+        *  Update the return address so that it 
+        *  will point after the interrupted MOVE.
+        */
+       SCR_REG_REG (ia, SCR_ADD, 8),
+               0,
+       SCR_REG_REG (ia1, SCR_ADDC, 0),
+               0,
+}/*-------------------------< PM_SAVE >--------------------------*/,{
+       /*
+        *  Clear all the flags that told us if we were 
+        *  interrupted in a PM DATA mini-script and/or 
+        *  we received a SAVE DP.
+        */
+       SCR_SFBR_REG (HF_REG, SCR_AND, (~(HF_IN_PM0|HF_IN_PM1|HF_DP_SAVED))),
+               0,
+       /*
+        *  Choose the current PM context.
+        */
+       SCR_JUMP ^ IFTRUE (MASK (HF_ACT_PM, HF_ACT_PM)),
+               PADDR_B (pm1_save),
+}/*-------------------------< PM0_SAVE >-------------------------*/,{
+       SCR_STORE_REL (ia, 4),
+               offsetof(struct sym_ccb, phys.pm0.ret),
+       /*
+        *  If WSR bit is set, either UA and RBC may 
+        *  have to be changed whether the device wants 
+        *  to ignore this residue or not.
+        */
+       SCR_FROM_REG (scntl2),
+               0,
+       SCR_CALL ^ IFTRUE (MASK (WSR, WSR)),
+               PADDR_B (pm_wsr_handle),
+       /*
+        *  Save the remaining byte count, the updated 
+        *  address and the return address.
+        */
+       SCR_STORE_REL (rbc, 4),
+               offsetof(struct sym_ccb, phys.pm0.sg.size),
+       SCR_STORE_REL (ua, 4),
+               offsetof(struct sym_ccb, phys.pm0.sg.addr),
+       /*
+        *  Set the current pointer at the PM0 DATA mini-script.
+        */
+       SCR_LOAD_ABS (ia, 4),
+               PADDR_B (pm0_data_addr),
+}/*-------------------------< PM_SAVE_END >----------------------*/,{
+       SCR_STORE_REL (ia, 4),
+               offsetof(struct sym_ccb, phys.head.lastp),
+       SCR_JUMP,
+               PADDR_A (dispatch),
+}/*-------------------------< PM1_SAVE >-------------------------*/,{
+       SCR_STORE_REL (ia, 4),
+               offsetof(struct sym_ccb, phys.pm1.ret),
+       /*
+        *  If WSR bit is set, either UA and RBC may 
+        *  have to be changed whether the device wants 
+        *  to ignore this residue or not.
+        */
+       SCR_FROM_REG (scntl2),
+               0,
+       SCR_CALL ^ IFTRUE (MASK (WSR, WSR)),
+               PADDR_B (pm_wsr_handle),
+       /*
+        *  Save the remaining byte count, the updated 
+        *  address and the return address.
+        */
+       SCR_STORE_REL (rbc, 4),
+               offsetof(struct sym_ccb, phys.pm1.sg.size),
+       SCR_STORE_REL (ua, 4),
+               offsetof(struct sym_ccb, phys.pm1.sg.addr),
+       /*
+        *  Set the current pointer at the PM1 DATA mini-script.
+        */
+       SCR_LOAD_ABS (ia, 4),
+               PADDR_B (pm1_data_addr),
+       SCR_JUMP,
+               PADDR_B (pm_save_end),
+}/*-------------------------< PM_WSR_HANDLE >--------------------*/,{
+       /*
+        *  Phase mismatch handling from SCRIPT with WSR set.
+        *  Such a condition can occur if the chip wants to 
+        *  execute a CHMOV(size > 1) when the WSR bit is 
+        *  set and the target changes PHASE.
+        *
+        *  We must move the residual byte to memory.
+        *
+        *  UA contains bit 0..31 of the address to 
+        *  move the residual byte.
+        *  Move it to the table indirect.
+        */
+       SCR_STORE_REL (ua, 4),
+               offsetof (struct sym_ccb, phys.wresid.addr),
+       /*
+        *  Increment UA (move address to next position).
+        */
+       SCR_REG_REG (ua, SCR_ADD, 1),
+               0,
+       SCR_REG_REG (ua1, SCR_ADDC, 0),
+               0,
+       SCR_REG_REG (ua2, SCR_ADDC, 0),
+               0,
+       SCR_REG_REG (ua3, SCR_ADDC, 0),
+               0,
+       /*
+        *  Compute SCRATCHA as:
+        *  - size to transfer = 1 byte.
+        *  - bit 24..31 = high address bit [32...39].
+        */
+       SCR_LOAD_ABS (scratcha, 4),
+               PADDR_B (zero),
+       SCR_REG_REG (scratcha, SCR_OR, 1),
+               0,
+       SCR_FROM_REG (rbc3),
+               0,
+       SCR_TO_REG (scratcha3),
+               0,
+       /*
+        *  Move this value to the table indirect.
+        */
+       SCR_STORE_REL (scratcha, 4),
+               offsetof (struct sym_ccb, phys.wresid.size),
+       /*
+        *  Wait for a valid phase.
+        *  While testing with bogus QUANTUM drives, the C1010 
+        *  sometimes raised a spurious phase mismatch with 
+        *  WSR and the CHMOV(1) triggered another PM.
+        *  Waiting explicitely for the PHASE seemed to avoid 
+        *  the nested phase mismatch. Btw, this didn't happen 
+        *  using my IBM drives.
+        */
+       SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_IN)),
+               0,
+       /*
+        *  Perform the move of the residual byte.
+        */
+       SCR_CHMOV_TBL ^ SCR_DATA_IN,
+               offsetof (struct sym_ccb, phys.wresid),
+       /*
+        *  We can now handle the phase mismatch with UA fixed.
+        *  RBC[0..23]=0 is a special case that does not require 
+        *  a PM context. The C code also checks against this.
+        */
+       SCR_FROM_REG (rbc),
+               0,
+       SCR_RETURN ^ IFFALSE (DATA (0)),
+               0,
+       SCR_FROM_REG (rbc1),
+               0,
+       SCR_RETURN ^ IFFALSE (DATA (0)),
+               0,
+       SCR_FROM_REG (rbc2),
+               0,
+       SCR_RETURN ^ IFFALSE (DATA (0)),
+               0,
+       /*
+        *  RBC[0..23]=0.
+        *  Not only we donnot need a PM context, but this would 
+        *  lead to a bogus CHMOV(0). This condition means that 
+        *  the residual was the last byte to move from this CHMOV.
+        *  So, we just have to move the current data script pointer 
+        *  (i.e. TEMP) to the SCRIPTS address following the 
+        *  interrupted CHMOV and jump to dispatcher.
+        *  IA contains the data pointer to save.
+        */
+       SCR_JUMP,
+               PADDR_B (pm_save_end),
+}/*-------------------------< WSR_MA_HELPER >--------------------*/,{
+       /*
+        *  Helper for the C code when WSR bit is set.
+        *  Perform the move of the residual byte.
+        */
+       SCR_CHMOV_TBL ^ SCR_DATA_IN,
+               offsetof (struct sym_ccb, phys.wresid),
+       SCR_JUMP,
+               PADDR_A (dispatch),
+
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+}/*-------------------------< DATA_IO >--------------------------*/,{
+       /*
+        *  We jump here if the data direction was unknown at the 
+        *  time we had to queue the command to the scripts processor.
+        *  Pointers had been set as follow in this situation:
+        *    savep   -->   DATA_IO
+        *    lastp   -->   start pointer when DATA_IN
+        *    wlastp  -->   start pointer when DATA_OUT
+        *  This script sets savep and lastp according to the 
+        *  direction chosen by the target.
+        */
+       SCR_JUMP ^ IFTRUE (WHEN (SCR_DATA_OUT)),
+               PADDR_B (data_io_out),
+}/*-------------------------< DATA_IO_IN >-----------------------*/,{
+       /*
+        *  Direction is DATA IN.
+        */
+       SCR_LOAD_REL  (scratcha, 4),
+               offsetof (struct sym_ccb, phys.head.lastp),
+}/*-------------------------< DATA_IO_COM >----------------------*/,{
+       SCR_STORE_REL (scratcha, 4),
+               offsetof (struct sym_ccb, phys.head.savep),
+
+       /*
+        *  Jump to the SCRIPTS according to actual direction.
+        */
+       SCR_LOAD_REL  (temp, 4),
+               offsetof (struct sym_ccb, phys.head.savep),
+       SCR_RETURN,
+               0,
+}/*-------------------------< DATA_IO_OUT >----------------------*/,{
+       /*
+        *  Direction is DATA OUT.
+        */
+       SCR_REG_REG (HF_REG, SCR_AND, (~HF_DATA_IN)),
+               0,
+       SCR_LOAD_REL  (scratcha, 4),
+               offsetof (struct sym_ccb, phys.head.wlastp),
+       SCR_STORE_REL (scratcha, 4),
+               offsetof (struct sym_ccb, phys.head.lastp),
+       SCR_JUMP,
+               PADDR_B(data_io_com),
+#endif /* SYM_OPT_HANDLE_DIR_UNKNOWN */
+
+}/*-------------------------< ZERO >-----------------------------*/,{
+       SCR_DATA_ZERO,
+}/*-------------------------< SCRATCH >--------------------------*/,{
+       SCR_DATA_ZERO,
+}/*-------------------------< PM0_DATA_ADDR >--------------------*/,{
+       SCR_DATA_ZERO,
+}/*-------------------------< PM1_DATA_ADDR >--------------------*/,{
+       SCR_DATA_ZERO,
+}/*-------------------------< DONE_POS >-------------------------*/,{
+       SCR_DATA_ZERO,
+}/*-------------------------< STARTPOS >-------------------------*/,{
+       SCR_DATA_ZERO,
+}/*-------------------------< TARGTBL >--------------------------*/,{
+       SCR_DATA_ZERO,
+}/*-------------------------<>-----------------------------------*/
+};
+
+static struct SYM_FWZ_SCR SYM_FWZ_SCR = {
+ /*-------------------------< SNOOPTEST >------------------------*/{
+       /*
+        *  Read the variable from memory.
+        */
+       SCR_LOAD_REL (scratcha, 4),
+               offsetof(struct sym_hcb, scratch),
+       /*
+        *  Write the variable to memory.
+        */
+       SCR_STORE_REL (temp, 4),
+               offsetof(struct sym_hcb, scratch),
+       /*
+        *  Read back the variable from memory.
+        */
+       SCR_LOAD_REL (temp, 4),
+               offsetof(struct sym_hcb, scratch),
+}/*-------------------------< SNOOPEND >-------------------------*/,{
+       /*
+        *  And stop.
+        */
+       SCR_INT,
+               99,
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+       /*
+        *  We may use MEMORY MOVE instructions to load the on chip-RAM,
+        *  if it happens that mapping PCI memory is not possible.
+        *  But writing the RAM from the CPU is the preferred method, 
+        *  since PCI 2.2 seems to disallow PCI self-mastering.
+        */
+}/*-------------------------< START_RAM >------------------------*/,{
+       /*
+        *  Load the script into on-chip RAM, 
+        *  and jump to start point.
+        */
+       SCR_COPY (sizeof(struct SYM_FWA_SCR)),
+}/*-------------------------< SCRIPTA0_BA >----------------------*/,{
+               0,
+               PADDR_A (start),
+       SCR_JUMP,
+               PADDR_A (init),
+}/*-------------------------< START_RAM64 >----------------------*/,{
+       /*
+        *  Load the RAM and start for 64 bit PCI (895A,896).
+        *  Both scripts (script and scripth) are loaded into 
+        *  the RAM which is 8K (4K for 825A/875/895).
+        *  We also need to load some 32-63 bit segments 
+        *  address of the SCRIPTS processor.
+        *  LOAD/STORE ABSOLUTE always refers to on-chip RAM 
+        *  in our implementation. The main memory is 
+        *  accessed using LOAD/STORE DSA RELATIVE.
+        */
+       SCR_LOAD_REL (mmws, 4),
+               offsetof (struct sym_hcb, scr_ram_seg),
+       SCR_COPY (sizeof(struct SYM_FWA_SCR)),
+}/*-------------------------< SCRIPTA0_BA64 >--------------------*/,{
+               0,
+               PADDR_A (start),
+       SCR_COPY (sizeof(struct SYM_FWB_SCR)),
+}/*-------------------------< SCRIPTB0_BA64 >--------------------*/,{
+               0,
+               PADDR_B  (start64),
+       SCR_LOAD_REL (mmrs, 4),
+               offsetof (struct sym_hcb, scr_ram_seg),
+       SCR_JUMP64,
+               PADDR_B (start64),
+}/*-------------------------< RAM_SEG64 >------------------------*/,{
+               0,
+#endif /* SYM_OPT_NO_BUS_MEMORY_MAPPING */
+}/*-------------------------<>-----------------------------------*/
+};
diff --git a/xen/drivers/scsi/sym53c8xx_2/sym_glue.c b/xen/drivers/scsi/sym53c8xx_2/sym_glue.c
new file mode 100644 (file)
index 0000000..23db7c2
--- /dev/null
@@ -0,0 +1,3049 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000  Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ *         Wolfgang Stanglmeier        <wolf@cologne.de>
+ *         Stefan Esser                <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994  Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#define SYM_GLUE_C
+
+#include <linux/module.h>
+#include "sym_glue.h"
+
+#define NAME53C                "sym53c"
+#define NAME53C8XX     "sym53c8xx"
+
+/*
+ *  Simple Wrapper to kernel PCI bus interface.
+ */
+
+typedef struct pci_dev *pcidev_t;
+#define PCIDEV_NULL            (0)
+#define PciBusNumber(d)                (d)->bus->number
+#define PciDeviceFn(d)         (d)->devfn
+#define PciVendorId(d)         (d)->vendor
+#define PciDeviceId(d)         (d)->device
+#define PciIrqLine(d)          (d)->irq
+
+static u_long __init
+pci_get_base_cookie(struct pci_dev *pdev, int index)
+{
+       u_long base;
+
+#if LINUX_VERSION_CODE > LinuxVersionCode(2,3,12)
+       base = pdev->resource[index].start;
+#else
+       base = pdev->base_address[index];
+#if BITS_PER_LONG > 32
+       if ((base & 0x7) == 0x4)
+               base |= (((u_long)pdev->base_address[++index]) << 32);
+#endif
+#endif
+       return (base & ~0x7ul);
+}
+
+static int __init
+pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
+{
+       u32 tmp;
+#define PCI_BAR_OFFSET(index) (PCI_BASE_ADDRESS_0 + (index<<2))
+
+       pci_read_config_dword(pdev, PCI_BAR_OFFSET(index), &tmp);
+       *base = tmp;
+       ++index;
+       if ((tmp & 0x7) == 0x4) {
+#if BITS_PER_LONG > 32
+               pci_read_config_dword(pdev, PCI_BAR_OFFSET(index), &tmp);
+               *base |= (((u_long)tmp) << 32);
+#endif
+               ++index;
+       }
+       return index;
+#undef PCI_BAR_OFFSET
+}
+
+#if LINUX_VERSION_CODE  < LinuxVersionCode(2,4,0)
+#define pci_enable_device(pdev)                (0)
+#endif
+
+#if LINUX_VERSION_CODE  < LinuxVersionCode(2,4,4)
+#define scsi_set_pci_device(inst, pdev)        do { ;} while (0)
+#endif
+
+/*
+ *  Insert a delay in micro-seconds and milli-seconds.
+ */
+void sym_udelay(int us) { udelay(us); }
+void sym_mdelay(int ms) { mdelay(ms); }
+
+/*
+ *  SMP threading.
+ *
+ *  The whole SCSI sub-system under Linux is basically single-threaded.
+ *  Everything, including low-level driver interrupt routine, happens 
+ *  with the `io_request_lock' held.
+ *  The sym53c8xx-1.x drivers series ran their interrupt code using a 
+ *  spin mutex per controller. This added complexity without improving 
+ *  scalability significantly. the sym-2 driver still use a spinlock 
+ *  per controller for safety, but basically runs with the damned 
+ *  io_request_lock held.
+ */
+
+spinlock_t sym53c8xx_lock = SPIN_LOCK_UNLOCKED;
+
+#define        SYM_LOCK_DRIVER(flags)    spin_lock_irqsave(&sym53c8xx_lock, flags)
+#define        SYM_UNLOCK_DRIVER(flags)  spin_unlock_irqrestore(&sym53c8xx_lock,flags)
+
+#define SYM_INIT_LOCK_HCB(np)     spin_lock_init(&np->s.smp_lock);
+#define        SYM_LOCK_HCB(np, flags)   spin_lock_irqsave(&np->s.smp_lock, flags)
+#define        SYM_UNLOCK_HCB(np, flags) spin_unlock_irqrestore(&np->s.smp_lock, flags)
+
+#define        SYM_LOCK_SCSI(np, flags) \
+               spin_lock_irqsave(&io_request_lock, flags)
+#define        SYM_UNLOCK_SCSI(np, flags) \
+               spin_unlock_irqrestore(&io_request_lock, flags)
+
+/* Ugly, but will make things easier if this locking will ever disappear */
+#define        SYM_LOCK_SCSI_NOSAVE(np)        spin_lock_irq(&io_request_lock)
+#define        SYM_UNLOCK_SCSI_NORESTORE(np)   spin_unlock_irq(&io_request_lock)
+
+/*
+ *  These simple macros limit expression involving 
+ *  kernel time values (jiffies) to some that have 
+ *  chance not to be too much incorrect. :-)
+ */
+#define ktime_get(o)           (jiffies + (u_long) o)
+#define ktime_exp(b)           ((long)(jiffies) - (long)(b) >= 0)
+#define ktime_dif(a, b)                ((long)(a) - (long)(b))
+#define ktime_add(a, o)                ((a) + (u_long)(o))
+#define ktime_sub(a, o)                ((a) - (u_long)(o))
+
+/*
+ *  Wrappers to the generic memory allocator.
+ */
+void *sym_calloc(int size, char *name)
+{
+       u_long flags;
+       void *m;
+       SYM_LOCK_DRIVER(flags);
+       m = sym_calloc_unlocked(size, name);
+       SYM_UNLOCK_DRIVER(flags);
+       return m;
+}
+
+void sym_mfree(void *m, int size, char *name)
+{
+       u_long flags;
+       SYM_LOCK_DRIVER(flags);
+       sym_mfree_unlocked(m, size, name);
+       SYM_UNLOCK_DRIVER(flags);
+}
+
+#ifdef SYM_LINUX_DYNAMIC_DMA_MAPPING
+
+void *__sym_calloc_dma(m_pool_ident_t dev_dmat, int size, char *name)
+{
+       u_long flags;
+       void *m;
+       SYM_LOCK_DRIVER(flags);
+       m = __sym_calloc_dma_unlocked(dev_dmat, size, name);
+       SYM_UNLOCK_DRIVER(flags);
+       return m;
+}
+
+void __sym_mfree_dma(m_pool_ident_t dev_dmat, void *m, int size, char *name)
+{
+       u_long flags;
+       SYM_LOCK_DRIVER(flags);
+       __sym_mfree_dma_unlocked(dev_dmat, m, size, name);
+       SYM_UNLOCK_DRIVER(flags);
+}
+
+m_addr_t __vtobus(m_pool_ident_t dev_dmat, void *m)
+{
+       u_long flags;
+       m_addr_t b;
+       SYM_LOCK_DRIVER(flags);
+       b = __vtobus_unlocked(dev_dmat, m);
+       SYM_UNLOCK_DRIVER(flags);
+       return b;
+}
+
+#endif /* SYM_LINUX_DYNAMIC_DMA_MAPPING */
+
+
+/*
+ *  Map/unmap a PCI memory window.
+ */
+#ifndef SYM_OPT_NO_BUS_MEMORY_MAPPING
+static u_long __init pci_map_mem(u_long base, u_long size)
+{
+       u_long page_base        = ((u_long) base) & PAGE_MASK;
+       u_long page_offs        = ((u_long) base) - page_base;
+       u_long page_remapped    = (u_long) ioremap(page_base, page_offs+size);
+
+       return page_remapped? (page_remapped + page_offs) : 0UL;
+}
+
+static void __init pci_unmap_mem(u_long vaddr, u_long size)
+{
+       if (vaddr)
+               iounmap((void *) (vaddr & PAGE_MASK));
+}
+#endif
+
+/*
+ *  Used to retrieve the host structure when the 
+ *  driver is called from the proc FS.
+ */
+static struct Scsi_Host        *first_host = NULL;
+
+/*
+ *  /proc directory entry and proc_info.
+ */
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+static struct proc_dir_entry proc_scsi_sym53c8xx = {
+    PROC_SCSI_SYM53C8XX, 9, NAME53C8XX,
+    S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+#endif
+
+/*
+ *  Transfer direction
+ *
+ *  Until some linux kernel version near 2.3.40, low-level scsi 
+ *  drivers were not told about data transfer direction.
+ */
+#if LINUX_VERSION_CODE > LinuxVersionCode(2, 3, 40)
+
+#define scsi_data_direction(cmd)       (cmd->sc_data_direction)
+
+#else
+
+static __inline__ int scsi_data_direction(Scsi_Cmnd *cmd)
+{
+       int direction;
+
+       switch((int) cmd->cmnd[0]) {
+       case 0x08:  /*  READ(6)                         08 */
+       case 0x28:  /*  READ(10)                        28 */
+       case 0xA8:  /*  READ(12)                        A8 */
+               direction = SCSI_DATA_READ;
+               break;
+       case 0x0A:  /*  WRITE(6)                        0A */
+       case 0x2A:  /*  WRITE(10)                       2A */
+       case 0xAA:  /*  WRITE(12)                       AA */
+               direction = SCSI_DATA_WRITE;
+               break;
+       default:
+               direction = SCSI_DATA_UNKNOWN;
+               break;
+       }
+
+       return direction;
+}
+
+#endif
+
+/*
+ *  Driver host data structure.
+ */
+struct host_data {
+     hcb_p ncb;
+};
+
+/*
+ * Some type that fit DMA addresses as seen from BUS.
+ */
+#ifndef SYM_LINUX_DYNAMIC_DMA_MAPPING
+typedef u_long         bus_addr_t;
+#else
+#if    SYM_CONF_DMA_ADDRESSING_MODE > 0
+typedef dma64_addr_t   bus_addr_t;
+#else
+typedef dma_addr_t     bus_addr_t;
+#endif
+#endif
+
+/*
+ *  Used by the eh thread to wait for command completion.
+ *  It is allocated on the eh thread stack.
+ */
+struct sym_eh_wait {
+/* SAE: */
+#ifdef XENO_KILLED
+       struct semaphore sem;
+#endif
+       struct timer_list timer;
+       void (*old_done)(Scsi_Cmnd *);
+       int to_do;
+       int timed_out;
+};
+
+/*
+ *  Driver private area in the SCSI command structure.
+ */
+struct sym_ucmd {              /* Override the SCSI pointer structure */
+       SYM_QUEHEAD link_cmdq;  /* Must stay at offset ZERO */
+#ifdef SYM_LINUX_DYNAMIC_DMA_MAPPING
+       bus_addr_t data_mapping;
+       u_char  data_mapped;
+#endif
+       struct sym_eh_wait *eh_wait;
+};
+
+typedef struct sym_ucmd *ucmd_p;
+
+#define SYM_UCMD_PTR(cmd)  ((ucmd_p)(&(cmd)->SCp))
+#define SYM_SCMD_PTR(ucmd) sym_que_entry(ucmd, Scsi_Cmnd, SCp)
+#define SYM_SOFTC_PTR(cmd) (((struct host_data *)cmd->host->hostdata)->ncb)
+
+/*
+ *  Deal with DMA mapping/unmapping.
+ */
+
+#ifndef SYM_LINUX_DYNAMIC_DMA_MAPPING
+
+/* Linux versions prior to pci bus iommu kernel interface */
+
+#define __unmap_scsi_data(pdev, cmd)   do {; } while (0)
+#define __map_scsi_single_data(pdev, cmd) (__vtobus(pdev,(cmd)->request_buffer))
+#define __map_scsi_sg_data(pdev, cmd)  ((cmd)->use_sg)
+#define __sync_scsi_data(pdev, cmd)    do {; } while (0)
+
+#define bus_sg_dma_address(sc)         vtobus((sc)->address)
+#define bus_sg_dma_len(sc)             ((sc)->length)
+
+#else /* Linux version with pci bus iommu kernel interface */
+
+#define        bus_unmap_sg(pdev, sgptr, sgcnt, dir)           \
+       pci_unmap_sg(pdev, sgptr, sgcnt, dir)
+
+#define        bus_unmap_single(pdev, mapping, bufptr, dir)    \
+       pci_unmap_single(pdev, mapping, bufptr, dir)
+
+#define        bus_map_single(pdev, bufptr, bufsiz, dir)       \
+       pci_map_single(pdev, bufptr, bufsiz, dir)
+#define        bus_map_sg(pdev, sgptr, sgcnt, dir)             \
+       pci_map_sg(pdev, sgptr, sgcnt, dir)
+
+#define        bus_dma_sync_sg(pdev, sgptr, sgcnt, dir)        \
+       pci_dma_sync_sg(pdev, sgptr, sgcnt, dir)
+
+#define        bus_dma_sync_single(pdev, mapping, bufsiz, dir) \
+       pci_dma_sync_single(pdev, mapping, bufsiz, dir)
+
+#define bus_sg_dma_address(sc) sg_dma_address(sc)
+#define bus_sg_dma_len(sc)     sg_dma_len(sc)
+
+static void __unmap_scsi_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+       int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+       switch(SYM_UCMD_PTR(cmd)->data_mapped) {
+       case 2:
+               bus_unmap_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+               break;
+       case 1:
+               bus_unmap_single(pdev, SYM_UCMD_PTR(cmd)->data_mapping,
+                                cmd->request_bufflen, dma_dir);
+               break;
+       }
+       SYM_UCMD_PTR(cmd)->data_mapped = 0;
+}
+
+static bus_addr_t __map_scsi_single_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+       bus_addr_t mapping;
+       int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+       mapping = bus_map_single(pdev, cmd->request_buffer,
+                                cmd->request_bufflen, dma_dir);
+       if (mapping) {
+               SYM_UCMD_PTR(cmd)->data_mapped  = 1;
+               SYM_UCMD_PTR(cmd)->data_mapping = mapping;
+       }
+
+       return mapping;
+}
+
+static int __map_scsi_sg_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+       int use_sg;
+       int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+       use_sg = bus_map_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+       if (use_sg > 0) {
+               SYM_UCMD_PTR(cmd)->data_mapped  = 2;
+               SYM_UCMD_PTR(cmd)->data_mapping = use_sg;
+       }
+
+       return use_sg;
+}
+
+static void __sync_scsi_data(pcidev_t pdev, Scsi_Cmnd *cmd)
+{
+       int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+       switch(SYM_UCMD_PTR(cmd)->data_mapped) {
+       case 2:
+               bus_dma_sync_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+               break;
+       case 1:
+               bus_dma_sync_single(pdev, SYM_UCMD_PTR(cmd)->data_mapping,
+                                   cmd->request_bufflen, dma_dir);
+               break;
+       }
+}
+
+#endif /* SYM_LINUX_DYNAMIC_DMA_MAPPING */
+
+#define unmap_scsi_data(np, cmd)       \
+               __unmap_scsi_data(np->s.device, cmd)
+#define map_scsi_single_data(np, cmd)  \
+               __map_scsi_single_data(np->s.device, cmd)
+#define map_scsi_sg_data(np, cmd)      \
+               __map_scsi_sg_data(np->s.device, cmd)
+#define sync_scsi_data(np, cmd)                \
+               __sync_scsi_data(np->s.device, cmd)
+
+/*
+ *  Complete a pending CAM CCB.
+ */
+void sym_xpt_done(hcb_p np, Scsi_Cmnd *ccb)
+{
+       sym_remque(&SYM_UCMD_PTR(ccb)->link_cmdq);
+       unmap_scsi_data(np, ccb);
+       ccb->scsi_done(ccb);
+}
+
+void sym_xpt_done2(hcb_p np, Scsi_Cmnd *ccb, int cam_status)
+{
+       sym_set_cam_status(ccb, cam_status);
+       sym_xpt_done(np, ccb);
+}
+
+
+/*
+ *  Print something that identifies the IO.
+ */
+void sym_print_addr (ccb_p cp)
+{
+       Scsi_Cmnd *cmd = cp->cam_ccb;
+       if (cmd)
+               printf("%s:%d:%d:", sym_name(SYM_SOFTC_PTR(cmd)),
+                      cmd->target,cmd->lun);
+}
+
+/*
+ *  Tell the SCSI layer about a BUS RESET.
+ */
+void sym_xpt_async_bus_reset(hcb_p np)
+{
+       printf_notice("%s: SCSI BUS has been reset.\n", sym_name(np));
+       np->s.settle_time = ktime_get(sym_driver_setup.settle_delay * HZ);
+       np->s.settle_time_valid = 1;
+       if (sym_verbose >= 2)
+               printf_info("%s: command processing suspended for %d seconds\n",
+                           sym_name(np), sym_driver_setup.settle_delay);
+}
+
+/*
+ *  Tell the SCSI layer about a BUS DEVICE RESET message sent.
+ */
+void sym_xpt_async_sent_bdr(hcb_p np, int target)
+{
+       printf_notice("%s: TARGET %d has been reset.\n", sym_name(np), target);
+}
+
+/*
+ *  Tell the SCSI layer about the new transfer parameters.
+ */
+void sym_xpt_async_nego_wide(hcb_p np, int target)
+{
+       if (sym_verbose < 3)
+               return;
+       sym_announce_transfer_rate(np, target);
+}
+
+/*
+ *  Choose the more appropriate CAM status if 
+ *  the IO encountered an extended error.
+ */
+static int sym_xerr_cam_status(int cam_status, int x_status)
+{
+       if (x_status) {
+               if      (x_status & XE_PARITY_ERR)
+                       cam_status = DID_PARITY;
+               else if (x_status &(XE_EXTRA_DATA|XE_SODL_UNRUN|XE_SWIDE_OVRUN))
+                       cam_status = DID_ERROR;
+               else if (x_status & XE_BAD_PHASE)
+                       cam_status = DID_ERROR;
+               else
+                       cam_status = DID_ERROR;
+       }
+       return cam_status;
+}
+
+/*
+ *  Build CAM result for a failed or auto-sensed IO.
+ */
+void sym_set_cam_result_error(hcb_p np, ccb_p cp, int resid)
+{
+       Scsi_Cmnd *csio = cp->cam_ccb;
+       u_int cam_status, scsi_status, drv_status;
+
+       drv_status  = 0;
+       cam_status  = DID_OK;
+       scsi_status = cp->ssss_status;
+
+       if (cp->host_flags & HF_SENSE) {
+               scsi_status = cp->sv_scsi_status;
+               resid = cp->sv_resid;
+               if (sym_verbose && cp->sv_xerr_status)
+                       sym_print_xerr(cp, cp->sv_xerr_status);
+               if (cp->host_status == HS_COMPLETE &&
+                   cp->ssss_status == S_GOOD &&
+                   cp->xerr_status == 0) {
+                       cam_status = sym_xerr_cam_status(DID_OK,
+                                                        cp->sv_xerr_status);
+                       drv_status = DRIVER_SENSE;
+                       /*
+                        *  Bounce back the sense data to user.
+                        */
+                       bzero(&csio->sense_buffer, sizeof(csio->sense_buffer));
+                       bcopy(cp->sns_bbuf, csio->sense_buffer,
+                             MIN(sizeof(csio->sense_buffer),SYM_SNS_BBUF_LEN));
+#if 0
+                       /*
+                        *  If the device reports a UNIT ATTENTION condition 
+                        *  due to a RESET condition, we should consider all 
+                        *  disconnect CCBs for this unit as aborted.
+                        */
+                       if (1) {
+                               u_char *p;
+                               p  = (u_char *) csio->sense_data;
+                               if (p[0]==0x70 && p[2]==0x6 && p[12]==0x29)
+                                       sym_clear_tasks(np, DID_ABORT,
+                                                       cp->target,cp->lun, -1);
+                       }
+#endif
+               }
+               else
+                       cam_status = DID_ERROR;
+       }
+       else if (cp->host_status == HS_COMPLETE)        /* Bad SCSI status */
+               cam_status = DID_OK;
+       else if (cp->host_status == HS_SEL_TIMEOUT)     /* Selection timeout */
+               cam_status = DID_NO_CONNECT;
+       else if (cp->host_status == HS_UNEXPECTED)      /* Unexpected BUS FREE*/
+               cam_status = DID_ERROR;
+       else {                                          /* Extended error */
+               if (sym_verbose) {
+                       PRINT_ADDR(cp);
+                       printf ("COMMAND FAILED (%x %x %x).\n",
+                               cp->host_status, cp->ssss_status,
+                               cp->xerr_status);
+               }
+               /*
+                *  Set the most appropriate value for CAM status.
+                */
+               cam_status = sym_xerr_cam_status(DID_ERROR, cp->xerr_status);
+       }
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,99)
+       csio->resid = resid;
+#endif
+       csio->result = (drv_status << 24) + (cam_status << 16) + scsi_status;
+}
+
+
+/*
+ *  Called on successfull INQUIRY response.
+ */
+void sym_sniff_inquiry(hcb_p np, Scsi_Cmnd *cmd, int resid)
+{
+       int retv;
+
+       if (!cmd || cmd->use_sg)
+               return;
+
+       sync_scsi_data(np, cmd);
+       retv = __sym_sniff_inquiry(np, cmd->target, cmd->lun,
+                                  (u_char *) cmd->request_buffer,
+                                  cmd->request_bufflen - resid);
+       if (retv < 0)
+               return;
+       else if (retv)
+               sym_update_trans_settings(np, &np->target[cmd->target]);
+}
+
+/*
+ *  Build the scatter/gather array for an I/O.
+ */
+
+static int sym_scatter_no_sglist(hcb_p np, ccb_p cp, Scsi_Cmnd *cmd)
+{
+       struct sym_tblmove *data = &cp->phys.data[SYM_CONF_MAX_SG-1];
+       int segment;
+
+       cp->data_len = cmd->request_bufflen;
+
+       if (cmd->request_bufflen) {
+               bus_addr_t baddr = map_scsi_single_data(np, cmd);
+               if (baddr) {
+                       sym_build_sge(np, data, baddr, cmd->request_bufflen);
+                       segment = 1;
+               }
+               else
+                       segment = -2;
+       }
+       else
+               segment = 0;
+
+       return segment;
+}
+
+static int sym_scatter(hcb_p np, ccb_p cp, Scsi_Cmnd *cmd)
+{
+       int segment;
+       int use_sg = (int) cmd->use_sg;
+
+       cp->data_len = 0;
+
+       if (!use_sg)
+               segment = sym_scatter_no_sglist(np, cp, cmd);
+       else if (use_sg > SYM_CONF_MAX_SG)
+               segment = -1;
+       else if ((use_sg = map_scsi_sg_data(np, cmd)) > 0) {
+               struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
+               struct sym_tblmove *data;
+
+               data = &cp->phys.data[SYM_CONF_MAX_SG - use_sg];
+
+               for (segment = 0; segment < use_sg; segment++) {
+                       bus_addr_t baddr = bus_sg_dma_address(&scatter[segment]);
+                       unsigned int len = bus_sg_dma_len(&scatter[segment]);
+
+                       sym_build_sge(np, &data[segment], baddr, len);
+                       cp->data_len += len;
+               }
+       }
+       else
+               segment = -2;
+
+       return segment;
+}
+
+/*
+ *  Queue a SCSI command.
+ */
+static int sym_queue_command(hcb_p np, Scsi_Cmnd *ccb)
+{
+/*     Scsi_Device        *device    = ccb->device; */
+       tcb_p   tp;
+       lcb_p   lp;
+       ccb_p   cp;
+       int     order;
+
+       /*
+        *  Minimal checkings, so that we will not 
+        *  go outside our tables.
+        */
+       if (ccb->target == np->myaddr ||
+           ccb->target >= SYM_CONF_MAX_TARGET ||
+           ccb->lun    >= SYM_CONF_MAX_LUN) {
+               sym_xpt_done2(np, ccb, CAM_DEV_NOT_THERE);
+               return 0;
+        }
+
+       /*
+        *  Retreive the target descriptor.
+        */
+       tp = &np->target[ccb->target];
+
+       /*
+        *  Complete the 1st INQUIRY command with error 
+        *  condition if the device is flagged NOSCAN 
+        *  at BOOT in the NVRAM. This may speed up 
+        *  the boot and maintain coherency with BIOS 
+        *  device numbering. Clearing the flag allows 
+        *  user to rescan skipped devices later.
+        *  We also return error for devices not flagged 
+        *  for SCAN LUNS in the NVRAM since some mono-lun 
+        *  devices behave badly when asked for some non 
+        *  zero LUN. Btw, this is an absolute hack.:-)
+        */
+       if (ccb->cmnd[0] == 0x12 || ccb->cmnd[0] == 0x0) {
+               if ((tp->usrflags & SYM_SCAN_BOOT_DISABLED) ||
+                   ((tp->usrflags & SYM_SCAN_LUNS_DISABLED) && 
+                    ccb->lun != 0)) {
+                       tp->usrflags &= ~SYM_SCAN_BOOT_DISABLED;
+                       sym_xpt_done2(np, ccb, CAM_DEV_NOT_THERE);
+                       return 0;
+               }
+       }
+
+       /*
+        *  Select tagged/untagged.
+        */
+       lp = sym_lp(np, tp, ccb->lun);
+       order = (lp && lp->s.reqtags) ? M_SIMPLE_TAG : 0;
+
+       /*
+        *  Queue the SCSI IO.
+        */
+       cp = sym_get_ccb(np, ccb->target, ccb->lun, order);
+       if (!cp)
+               return 1;       /* Means resource shortage */
+       (void) sym_queue_scsiio(np, ccb, cp);
+       return 0;
+}
+
+/*
+ *  Setup buffers and pointers that address the CDB.
+ */
+static int __inline sym_setup_cdb(hcb_p np, Scsi_Cmnd *ccb, ccb_p cp)
+{
+       u32     cmd_ba;
+       int     cmd_len;
+
+       /*
+        *  CDB is 16 bytes max.
+        */
+       if (ccb->cmd_len > sizeof(cp->cdb_buf)) {
+               sym_set_cam_status(cp->cam_ccb, CAM_REQ_INVALID);
+               return -1;
+       }
+
+       bcopy(ccb->cmnd, cp->cdb_buf, ccb->cmd_len);
+       cmd_ba  = CCB_BA (cp, cdb_buf[0]);
+       cmd_len = ccb->cmd_len;
+
+       cp->phys.cmd.addr       = cpu_to_scr(cmd_ba);
+       cp->phys.cmd.size       = cpu_to_scr(cmd_len);
+
+       return 0;
+}
+
+/*
+ *  Setup pointers that address the data and start the I/O.
+ */
+int sym_setup_data_and_start(hcb_p np, Scsi_Cmnd *csio, ccb_p cp)
+{
+       int dir;
+       tcb_p tp = &np->target[cp->target];
+       lcb_p lp = sym_lp(np, tp, cp->lun);
+
+       /*
+        *  Build the CDB.
+        */
+       if (sym_setup_cdb(np, csio, cp))
+               goto out_abort;
+
+       /*
+        *  No direction means no data.
+        */
+       dir = scsi_data_direction(csio);
+       if (dir != SCSI_DATA_NONE) {
+               cp->segments = sym_scatter (np, cp, csio);
+               if (cp->segments < 0) {
+                       if (cp->segments == -2)
+                               sym_set_cam_status(csio, CAM_RESRC_UNAVAIL);
+                       else
+                               sym_set_cam_status(csio, CAM_REQ_TOO_BIG);
+                       goto out_abort;
+               }
+       }
+       else {
+               cp->data_len = 0;
+               cp->segments = 0;
+       }
+
+       /*
+        *  Set data pointers.
+        */
+       sym_setup_data_pointers(np, cp, dir);
+
+       /*
+        *  When `#ifed 1', the code below makes the driver 
+        *  panic on the first attempt to write to a SCSI device.
+        *  It is the first test we want to do after a driver 
+        *  change that does not seem obviously safe. :)
+        */
+#if 0
+       switch (cp->cdb_buf[0]) {
+       case 0x0A: case 0x2A: case 0xAA:
+               panic("XXXXXXXXXXXXX WRITE NOT YET ALLOWED XXXXXXXXXXXXXX\n");
+               MDELAY(10000);
+               break;
+       default:
+               break;
+       }
+#endif
+
+       /*
+        *      activate this job.
+        */
+       if (lp)
+               sym_start_next_ccbs(np, lp, 2);
+       else
+               sym_put_start_queue(np, cp);
+       return 0;
+
+out_abort:
+       sym_free_ccb(np, cp);
+       sym_xpt_done(np, csio);
+       return 0;
+}
+
+
+/*
+ *  timer daemon.
+ *
+ *  Misused to keep the driver running when
+ *  interrupts are not configured correctly.
+ */
+static void sym_timer (hcb_p np)
+{
+       u_long  thistime = ktime_get(0);
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2, 4, 0)
+       /*
+        *  If release process in progress, let's go
+        *  Set the release stage from 1 to 2 to synchronize
+        *  with the release process.
+        */
+
+       if (np->s.release_stage) {
+               if (np->s.release_stage == 1)
+                       np->s.release_stage = 2;
+               return;
+       }
+#endif
+
+       /*
+        *  Restart the timer.
+        */
+#ifdef SYM_CONF_PCIQ_BROKEN_INTR
+       np->s.timer.expires = ktime_get((HZ+99)/100);
+#else
+       np->s.timer.expires = ktime_get(SYM_CONF_TIMER_INTERVAL);
+#endif
+       add_timer(&np->s.timer);
+
+       /*
+        *  If we are resetting the ncr, wait for settle_time before 
+        *  clearing it. Then command processing will be resumed.
+        */
+       if (np->s.settle_time_valid) {
+               if (ktime_dif(np->s.settle_time, thistime) <= 0){
+                       if (sym_verbose >= 2 )
+                               printk("%s: command processing resumed\n",
+                                      sym_name(np));
+                       np->s.settle_time_valid = 0;
+               }
+               return;
+       }
+
+       /*
+        *      Nothing to do for now, but that may come.
+        */
+       if (np->s.lasttime + 4*HZ < thistime) {
+               np->s.lasttime = thistime;
+       }
+
+#ifdef SYM_CONF_PCIQ_MAY_MISS_COMPLETIONS
+       /*
+        *  Some way-broken PCI bridges may lead to 
+        *  completions being lost when the clearing 
+        *  of the INTFLY flag by the CPU occurs 
+        *  concurrently with the chip raising this flag.
+        *  If this ever happen, lost completions will 
+        * be reaped here.
+        */
+       sym_wakeup_done(np);
+#endif
+
+#ifdef SYM_CONF_PCIQ_BROKEN_INTR
+       if (INB(nc_istat) & (INTF|SIP|DIP)) {
+
+               /*
+               **      Process pending interrupts.
+               */
+               if (DEBUG_FLAGS & DEBUG_TINY) printk ("{");
+               sym_interrupt(np);
+               if (DEBUG_FLAGS & DEBUG_TINY) printk ("}");
+       }
+#endif /* SYM_CONF_PCIQ_BROKEN_INTR */
+}
+
+
+/*
+ *  PCI BUS error handler.
+ */
+void sym_log_bus_error(hcb_p np)
+{
+       u_short pci_sts;
+       pci_read_config_word(np->s.device, PCI_STATUS, &pci_sts);
+       if (pci_sts & 0xf900) {
+               pci_write_config_word(np->s.device, PCI_STATUS,
+                                        pci_sts);
+               printf("%s: PCI STATUS = 0x%04x\n",
+                       sym_name(np), pci_sts & 0xf900);
+       }
+}
+
+
+/*
+ *  Requeue awaiting commands.
+ */
+static void sym_requeue_awaiting_cmds(hcb_p np)
+{
+       Scsi_Cmnd *cmd;
+       ucmd_p ucp = SYM_UCMD_PTR(cmd);
+       SYM_QUEHEAD tmp_cmdq;
+       int sts;
+
+       sym_que_move(&np->s.wait_cmdq, &tmp_cmdq);
+
+       while ((ucp = (ucmd_p) sym_remque_head(&tmp_cmdq)) != 0) {
+               sym_insque_tail(&ucp->link_cmdq, &np->s.busy_cmdq);
+               cmd = SYM_SCMD_PTR(ucp);
+               sts = sym_queue_command(np, cmd);
+               if (sts) {
+                       sym_remque(&ucp->link_cmdq);
+                       sym_insque_head(&ucp->link_cmdq, &np->s.wait_cmdq);
+               }
+       }
+}
+
+/*
+ *  Linux entry point of the queuecommand() function
+ */
+int sym53c8xx_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+       hcb_p  np  = SYM_SOFTC_PTR(cmd);
+       ucmd_p ucp = SYM_UCMD_PTR(cmd);
+       u_long flags;
+       int sts = 0;
+
+       cmd->scsi_done     = done;
+       cmd->host_scribble = NULL;
+       memset(ucp, 0, sizeof(*ucp));
+
+       SYM_LOCK_HCB(np, flags);
+
+       /*
+        *  Shorten our settle_time if needed for 
+        *  this command not to time out.
+        */
+       if (np->s.settle_time_valid && cmd->timeout_per_command) {
+               u_long tlimit = ktime_get(cmd->timeout_per_command);
+               tlimit = ktime_sub(tlimit, SYM_CONF_TIMER_INTERVAL*2);
+               if (ktime_dif(np->s.settle_time, tlimit) > 0) {
+                       np->s.settle_time = tlimit;
+               }
+       }
+
+       if (np->s.settle_time_valid || !sym_que_empty(&np->s.wait_cmdq)) {
+               sym_insque_tail(&ucp->link_cmdq, &np->s.wait_cmdq);
+               goto out;
+       }
+
+       sym_insque_tail(&ucp->link_cmdq, &np->s.busy_cmdq);
+       sts = sym_queue_command(np, cmd);
+       if (sts) {
+               sym_remque(&ucp->link_cmdq);
+               sym_insque_tail(&ucp->link_cmdq, &np->s.wait_cmdq);
+       }
+out:
+       SYM_UNLOCK_HCB(np, flags);
+
+       return 0;
+}
+
+/*
+ *  Linux entry point of the interrupt handler.
+ */
+static void sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
+{
+       unsigned long flags;
+       unsigned long flags1;
+       hcb_p np = (hcb_p) dev_id;
+
+       if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("[");
+
+       SYM_LOCK_SCSI(np, flags1);
+       SYM_LOCK_HCB(np, flags);
+
+       sym_interrupt(np);
+
+       if (!sym_que_empty(&np->s.wait_cmdq) && !np->s.settle_time_valid)
+               sym_requeue_awaiting_cmds(np);
+
+       SYM_UNLOCK_HCB(np, flags);
+       SYM_UNLOCK_SCSI(np, flags1);
+
+       if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("]\n");
+}
+
+/*
+ *  Linux entry point of the timer handler
+ */
+static void sym53c8xx_timer(unsigned long npref)
+{
+       hcb_p np = (hcb_p) npref;
+       unsigned long flags;
+       unsigned long flags1;
+
+       SYM_LOCK_SCSI(np, flags1);
+       SYM_LOCK_HCB(np, flags);
+
+       sym_timer(np);
+
+       if (!sym_que_empty(&np->s.wait_cmdq) && !np->s.settle_time_valid)
+               sym_requeue_awaiting_cmds(np);
+
+       SYM_UNLOCK_HCB(np, flags);
+       SYM_UNLOCK_SCSI(np, flags1);
+}
+
+
+/*
+ *  What the eh thread wants us to perform.
+ */
+#define SYM_EH_ABORT           0
+#define SYM_EH_DEVICE_RESET    1
+#define SYM_EH_BUS_RESET       2
+#define SYM_EH_HOST_RESET      3
+
+/*
+ *  What we will do regarding the involved SCSI command.
+ */
+#define SYM_EH_DO_IGNORE       0
+#define SYM_EH_DO_COMPLETE     1
+#define SYM_EH_DO_WAIT         2
+
+/*
+ *  Our general completion handler.
+ */
+static void __sym_eh_done(Scsi_Cmnd *cmd, int timed_out)
+{
+       struct sym_eh_wait *ep = SYM_UCMD_PTR(cmd)->eh_wait;
+       if (!ep)
+               return;
+
+       /* Try to avoid a race here (not 100% safe) */
+       if (!timed_out) {
+               ep->timed_out = 0;
+               if (ep->to_do == SYM_EH_DO_WAIT && !del_timer(&ep->timer))
+                       return;
+       }
+
+       /* Revert everything */
+       SYM_UCMD_PTR(cmd)->eh_wait = 0;
+       cmd->scsi_done = ep->old_done;
+
+       /* Wake up the eh thread if it wants to sleep */
+/* SAE: No sleeping... */
+#ifdef XENO_KILLED
+       if (ep->to_do == SYM_EH_DO_WAIT)
+               up(&ep->sem);
+#endif
+
+}
+
+/*
+ *  scsi_done() alias when error recovery is in progress. 
+ */
+static void sym_eh_done(Scsi_Cmnd *cmd) { __sym_eh_done(cmd, 0); }
+
+/*
+ *  Some timeout handler to avoid waiting too long.
+ */
+static void sym_eh_timeout(u_long p) { __sym_eh_done((Scsi_Cmnd *)p, 1); }
+
+/*
+ *  Generic method for our eh processing.
+ *  The 'op' argument tells what we have to do.
+ */
+static int sym_eh_handler(int op, char *opname, Scsi_Cmnd *cmd)
+{
+       hcb_p np = SYM_SOFTC_PTR(cmd);
+       unsigned long flags;
+       SYM_QUEHEAD *qp;
+       int to_do = SYM_EH_DO_IGNORE;
+       int sts = -1;
+       struct sym_eh_wait eh, *ep = &eh;
+       char devname[20];
+
+       sprintf(devname, "%s:%d:%d", sym_name(np), cmd->target, cmd->lun);
+
+       printf_warning("%s: %s operation started.\n", devname, opname);
+
+       SYM_LOCK_HCB(np, flags);
+
+#if 0
+       /* This one should be the result of some race, thus to ignore */
+       if (cmd->serial_number != cmd->serial_number_at_timeout)
+               goto prepare;
+#endif
+
+       /* This one is not queued to the core driver -> to complete here */ 
+       FOR_EACH_QUEUED_ELEMENT(&np->s.wait_cmdq, qp) {
+               if (SYM_SCMD_PTR(qp) == cmd) {
+                       to_do = SYM_EH_DO_COMPLETE;
+                       goto prepare;
+               }
+       }
+
+       /* This one is queued in some place -> to wait for completion */
+       FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
+               ccb_p cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+               if (cp->cam_ccb == cmd) {
+                       to_do = SYM_EH_DO_WAIT;
+                       goto prepare;
+               }
+       }
+
+prepare:
+       /* Prepare stuff to either ignore, complete or wait for completion */
+       switch(to_do) {
+       default:
+       case SYM_EH_DO_IGNORE:
+               goto finish;
+               break;
+       case SYM_EH_DO_WAIT:
+/* SAE: */
+#ifdef XENO_KILLED
+#if LINUX_VERSION_CODE > LinuxVersionCode(2,3,0)
+               init_MUTEX_LOCKED(&ep->sem);
+#else
+               ep->sem = MUTEX_LOCKED;
+#endif
+#endif /* XENO_KILLED */
+               /* SAE: ??? */  
+               mdelay(10); 
+               /* fall through */
+       case SYM_EH_DO_COMPLETE:
+               ep->old_done = cmd->scsi_done;
+               cmd->scsi_done = sym_eh_done;
+               SYM_UCMD_PTR(cmd)->eh_wait = ep;
+       }
+
+       /* Try to proceed the operation we have been asked for */
+       sts = -1;
+       switch(op) {
+       case SYM_EH_ABORT:
+               sts = sym_abort_scsiio(np, cmd, 1);
+               break;
+       case SYM_EH_DEVICE_RESET:
+               sts = sym_reset_scsi_target(np, cmd->target);
+               break;
+       case SYM_EH_BUS_RESET:
+               sym_reset_scsi_bus(np, 1);
+               sts = 0;
+               break;
+       case SYM_EH_HOST_RESET:
+               sym_reset_scsi_bus(np, 0);
+               sym_start_up (np, 1);
+               sts = 0;
+               break;
+       default:
+               break;
+       }
+
+       /* On error, restore everything and cross fingers :) */
+       if (sts) {
+               SYM_UCMD_PTR(cmd)->eh_wait = 0;
+               cmd->scsi_done = ep->old_done;
+               to_do = SYM_EH_DO_IGNORE;
+       }
+
+finish:
+       ep->to_do = to_do;
+       /* Complete the command with locks held as required by the driver */
+       if (to_do == SYM_EH_DO_COMPLETE)
+               sym_xpt_done2(np, cmd, CAM_REQ_ABORTED);
+
+       SYM_UNLOCK_HCB(np, flags);
+
+       /* Wait for completion with locks released, as required by kernel */
+       if (to_do == SYM_EH_DO_WAIT) {
+/* SAE: */
+#ifdef XENO_KILLED
+               init_timer(&ep->timer);
+               ep->timer.expires = jiffies + (5*HZ);
+               ep->timer.function = sym_eh_timeout;
+               ep->timer.data = (u_long)cmd;
+               ep->timed_out = 1;      /* Be pessimistic for once :) */
+               add_timer(&ep->timer);
+               SYM_UNLOCK_SCSI_NORESTORE(np);
+               down(&ep->sem);
+               SYM_LOCK_SCSI_NOSAVE(np);
+               if (ep->timed_out)
+                       sts = -2;
+#endif
+               /* SAE: ?? */
+               mdelay(10);
+       }
+       printf_warning("%s: %s operation %s.\n", devname, opname,
+                       sts==0?"complete":sts==-2?"timed-out":"failed");
+       return sts? SCSI_FAILED : SCSI_SUCCESS;
+}
+
+
+/*
+ * Error handlers called from the eh thread (one thread per HBA).
+ */
+int sym53c8xx_eh_abort_handler(Scsi_Cmnd *cmd)
+{
+       return sym_eh_handler(SYM_EH_ABORT, "ABORT", cmd);
+}
+
+int sym53c8xx_eh_device_reset_handler(Scsi_Cmnd *cmd)
+{
+       return sym_eh_handler(SYM_EH_DEVICE_RESET, "DEVICE RESET", cmd);
+}
+
+int sym53c8xx_eh_bus_reset_handler(Scsi_Cmnd *cmd)
+{
+       return sym_eh_handler(SYM_EH_BUS_RESET, "BUS RESET", cmd);
+}
+
+int sym53c8xx_eh_host_reset_handler(Scsi_Cmnd *cmd)
+{
+       return sym_eh_handler(SYM_EH_HOST_RESET, "HOST RESET", cmd);
+}
+
+/*
+ *  Tune device queuing depth, according to various limits.
+ */
+static void 
+sym_tune_dev_queuing(hcb_p np, int target, int lun, u_short reqtags)
+{
+       tcb_p   tp = &np->target[target];
+       lcb_p   lp = sym_lp(np, tp, lun);
+       u_short oldtags;
+
+       if (!lp)
+               return;
+
+       oldtags = lp->s.reqtags;
+
+       if (reqtags > lp->s.scdev_depth)
+               reqtags = lp->s.scdev_depth;
+
+       lp->started_limit = reqtags ? reqtags : 2;
+       lp->started_max   = 1;
+       lp->s.reqtags     = reqtags;
+
+       if (reqtags != oldtags) {
+               printf_info("%s:%d:%d: "
+                        "tagged command queuing %s, command queue depth %d.\n",
+                         sym_name(np), target, lun,
+                         lp->s.reqtags ? "enabled" : "disabled",
+                         lp->started_limit);
+       }
+}
+
+#ifdef SYM_LINUX_BOOT_COMMAND_LINE_SUPPORT
+/*
+ *  Linux select queue depths function
+ */
+#define DEF_DEPTH      (sym_driver_setup.max_tag)
+#define ALL_TARGETS    -2
+#define NO_TARGET      -1
+#define ALL_LUNS       -2
+#define NO_LUN         -1
+
+static int device_queue_depth(hcb_p np, int target, int lun)
+{
+       int c, h, t, u, v;
+       char *p = sym_driver_setup.tag_ctrl;
+       char *ep;
+
+       h = -1;
+       t = NO_TARGET;
+       u = NO_LUN;
+       while ((c = *p++) != 0) {
+               v = simple_strtoul(p, &ep, 0);
+               switch(c) {
+               case '/':
+                       ++h;
+                       t = ALL_TARGETS;
+                       u = ALL_LUNS;
+                       break;
+               case 't':
+                       if (t != target)
+                               t = (target == v) ? v : NO_TARGET;
+                       u = ALL_LUNS;
+                       break;
+               case 'u':
+                       if (u != lun)
+                               u = (lun == v) ? v : NO_LUN;
+                       break;
+               case 'q':
+                       if (h == np->s.unit &&
+                               (t == ALL_TARGETS || t == target) &&
+                               (u == ALL_LUNS    || u == lun))
+                               return v;
+                       break;
+               case '-':
+                       t = ALL_TARGETS;
+                       u = ALL_LUNS;
+                       break;
+               default:
+                       break;
+               }
+               p = ep;
+       }
+       return DEF_DEPTH;
+}
+#else
+#define device_queue_depth(np, t, l)   (sym_driver_setup.max_tag)
+#endif /* SYM_LINUX_BOOT_COMMAND_LINE_SUPPORT */
+
+/*
+ * Linux entry point for device queue sizing.
+ */
+static void 
+sym53c8xx_select_queue_depths(struct Scsi_Host *host, 
+                              struct scsi_device *devlist)
+{
+       struct scsi_device *device;
+
+       for (device = devlist; device; device = device->next) {
+               hcb_p np;
+               tcb_p tp;
+               lcb_p lp;
+               int reqtags;
+
+               if (device->host != host)
+                       continue;
+
+               np = ((struct host_data *) host->hostdata)->ncb;
+               tp = &np->target[device->id];
+
+               /*
+                *  Get user settings for transfer parameters.
+                */
+               tp->inq_byte7_valid = (INQ7_SYNC|INQ7_WIDE16);
+               sym_update_trans_settings(np, tp);
+
+               /*
+                *  Allocate the LCB if not yet.
+                *  If it fail, we may well be in the sh*t. :)
+                */
+               lp = sym_alloc_lcb(np, device->id, device->lun);
+               if (!lp) {
+                       device->queue_depth = 1;
+                       continue;
+               }
+
+               /*
+                *  Get user flags.
+                */
+               lp->curr_flags = lp->user_flags;
+
+               /*
+                *  Select queue depth from driver setup.
+                *  Donnot use more than configured by user.
+                *  Use at least 2.
+                *  Donnot use more than our maximum.
+                */
+               reqtags = device_queue_depth(np, device->id, device->lun);
+               if (reqtags > tp->usrtags)
+                       reqtags = tp->usrtags;
+               if (!device->tagged_supported)
+                       reqtags = 0;
+#if 1 /* Avoid to locally queue commands for no good reasons */
+               if (reqtags > SYM_CONF_MAX_TAG)
+                       reqtags = SYM_CONF_MAX_TAG;
+               device->queue_depth = reqtags ? reqtags : 2;
+#else
+               device->queue_depth = reqtags ? SYM_CONF_MAX_TAG : 2;
+#endif
+               lp->s.scdev_depth = device->queue_depth;
+               sym_tune_dev_queuing(np, device->id, device->lun, reqtags);
+       }
+}
+
+/*
+ *  Linux entry point for info() function
+ */
+const char *sym53c8xx_info (struct Scsi_Host *host)
+{
+       return sym_driver_name();
+}
+
+
+#ifdef SYM_LINUX_PROC_INFO_SUPPORT
+/*
+ *  Proc file system stuff
+ *
+ *  A read operation returns adapter information.
+ *  A write operation is a control command.
+ *  The string is parsed in the driver code and the command is passed 
+ *  to the sym_usercmd() function.
+ */
+
+#ifdef SYM_LINUX_USER_COMMAND_SUPPORT
+
+struct sym_usrcmd {
+       u_long  target;
+       u_long  lun;
+       u_long  data;
+       u_long  cmd;
+};
+
+#define UC_SETSYNC      10
+#define UC_SETTAGS     11
+#define UC_SETDEBUG    12
+#define UC_SETWIDE     14
+#define UC_SETFLAG     15
+#define UC_SETVERBOSE  17
+#define UC_RESETDEV    18
+#define UC_CLEARDEV    19
+
+static void sym_exec_user_command (hcb_p np, struct sym_usrcmd *uc)
+{
+       tcb_p tp;
+       int t, l;
+
+       switch (uc->cmd) {
+       case 0: return;
+
+#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT
+       case UC_SETDEBUG:
+               sym_debug_flags = uc->data;
+               break;
+#endif
+       case UC_SETVERBOSE:
+               np->verbose = uc->data;
+               break;
+       default:
+               /*
+                * We assume that other commands apply to targets.
+                * This should always be the case and avoid the below 
+                * 4 lines to be repeated 6 times.
+                */
+               for (t = 0; t < SYM_CONF_MAX_TARGET; t++) {
+                       if (!((uc->target >> t) & 1))
+                               continue;
+                       tp = &np->target[t];
+
+                       switch (uc->cmd) {
+
+                       case UC_SETSYNC:
+                               if (!uc->data || uc->data >= 255) {
+                                       tp->tinfo.goal.options = 0;
+                                       tp->tinfo.goal.offset  = 0;
+                                       break;
+                               }
+                               if (uc->data <= 9 && np->minsync_dt) {
+                                       if (uc->data < np->minsync_dt)
+                                               uc->data = np->minsync_dt;
+                                       tp->tinfo.goal.options = PPR_OPT_DT;
+                                       tp->tinfo.goal.width   = 1;
+                                       tp->tinfo.goal.period = uc->data;
+                                       tp->tinfo.goal.offset = np->maxoffs_dt;
+                               }
+                               else {
+                                       if (uc->data < np->minsync)
+                                               uc->data = np->minsync;
+                                       tp->tinfo.goal.options = 0;
+                                       tp->tinfo.goal.period = uc->data;
+                                       tp->tinfo.goal.offset = np->maxoffs;
+                               }
+                               break;
+                       case UC_SETWIDE:
+                               tp->tinfo.goal.width = uc->data ? 1 : 0;
+                               break;
+                       case UC_SETTAGS:
+                               for (l = 0; l < SYM_CONF_MAX_LUN; l++)
+                                       sym_tune_dev_queuing(np, t,l, uc->data);
+                               break;
+                       case UC_RESETDEV:
+                               tp->to_reset = 1;
+                               np->istat_sem = SEM;
+                               OUTB (nc_istat, SIGP|SEM);
+                               break;
+                       case UC_CLEARDEV:
+                               for (l = 0; l < SYM_CONF_MAX_LUN; l++) {
+                                       lcb_p lp = sym_lp(np, tp, l);
+                                       if (lp) lp->to_clear = 1;
+                               }
+                               np->istat_sem = SEM;
+                               OUTB (nc_istat, SIGP|SEM);
+                               break;
+                       case UC_SETFLAG:
+                               tp->usrflags = uc->data;
+                               break;
+                       }
+               }
+               break;
+       }
+}
+
+#define is_digit(c)    ((c) >= '0' && (c) <= '9')
+#define digit_to_bin(c)        ((c) - '0')
+#define is_space(c)    ((c) == ' ' || (c) == '\t')
+
+static int skip_spaces(char *ptr, int len)
+{
+       int cnt, c;
+
+       for (cnt = len; cnt > 0 && (c = *ptr++) && is_space(c); cnt--);
+
+       return (len - cnt);
+}
+
+static int get_int_arg(char *ptr, int len, u_long *pv)
+{
+       int     cnt, c;
+       u_long  v;
+
+       for (v = 0, cnt = len; cnt > 0 && (c = *ptr++) && is_digit(c); cnt--) {
+               v = (v * 10) + digit_to_bin(c);
+       }
+
+       if (pv)
+               *pv = v;
+
+       return (len - cnt);
+}
+
+static int is_keyword(char *ptr, int len, char *verb)
+{
+       int verb_len = strlen(verb);
+
+       if (len >= strlen(verb) && !memcmp(verb, ptr, verb_len))
+               return verb_len;
+       else
+               return 0;
+
+}
+
+#define SKIP_SPACES(min_spaces)                                                \
+       if ((arg_len = skip_spaces(ptr, len)) < (min_spaces))           \
+               return -EINVAL;                                         \
+       ptr += arg_len; len -= arg_len;
+
+#define GET_INT_ARG(v)                                                 \
+       if (!(arg_len = get_int_arg(ptr, len, &(v))))                   \
+               return -EINVAL;                                         \
+       ptr += arg_len; len -= arg_len;
+
+
+/*
+ * Parse a control command
+ */
+
+static int sym_user_command(hcb_p np, char *buffer, int length)
+{
+       char *ptr       = buffer;
+       int len         = length;
+       struct sym_usrcmd cmd, *uc = &cmd;
+       int             arg_len;
+       u_long          target;
+
+       bzero(uc, sizeof(*uc));
+
+       if (len > 0 && ptr[len-1] == '\n')
+               --len;
+
+       if      ((arg_len = is_keyword(ptr, len, "setsync")) != 0)
+               uc->cmd = UC_SETSYNC;
+       else if ((arg_len = is_keyword(ptr, len, "settags")) != 0)
+               uc->cmd = UC_SETTAGS;
+       else if ((arg_len = is_keyword(ptr, len, "setverbose")) != 0)
+               uc->cmd = UC_SETVERBOSE;
+       else if ((arg_len = is_keyword(ptr, len, "setwide")) != 0)
+               uc->cmd = UC_SETWIDE;
+#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT
+       else if ((arg_len = is_keyword(ptr, len, "setdebug")) != 0)
+               uc->cmd = UC_SETDEBUG;
+#endif
+       else if ((arg_len = is_keyword(ptr, len, "setflag")) != 0)
+               uc->cmd = UC_SETFLAG;
+       else if ((arg_len = is_keyword(ptr, len, "resetdev")) != 0)
+               uc->cmd = UC_RESETDEV;
+       else if ((arg_len = is_keyword(ptr, len, "cleardev")) != 0)
+               uc->cmd = UC_CLEARDEV;
+       else
+               arg_len = 0;
+
+#ifdef DEBUG_PROC_INFO
+printk("sym_user_command: arg_len=%d, cmd=%ld\n", arg_len, uc->cmd);
+#endif
+
+       if (!arg_len)
+               return -EINVAL;
+       ptr += arg_len; len -= arg_len;
+
+       switch(uc->cmd) {
+       case UC_SETSYNC:
+       case UC_SETTAGS:
+       case UC_SETWIDE:
+       case UC_SETFLAG:
+       case UC_RESETDEV:
+       case UC_CLEARDEV:
+               SKIP_SPACES(1);
+               if ((arg_len = is_keyword(ptr, len, "all")) != 0) {
+                       ptr += arg_len; len -= arg_len;
+                       uc->target = ~0;
+               } else {
+                       GET_INT_ARG(target);
+                       uc->target = (1<<target);
+#ifdef DEBUG_PROC_INFO
+printk("sym_user_command: target=%ld\n", target);
+#endif
+               }
+               break;
+       }
+
+       switch(uc->cmd) {
+       case UC_SETVERBOSE:
+       case UC_SETSYNC:
+       case UC_SETTAGS:
+       case UC_SETWIDE:
+               SKIP_SPACES(1);
+               GET_INT_ARG(uc->data);
+#ifdef DEBUG_PROC_INFO
+printk("sym_user_command: data=%ld\n", uc->data);
+#endif
+               break;
+#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT
+       case UC_SETDEBUG:
+               while (len > 0) {
+                       SKIP_SPACES(1);
+                       if      ((arg_len = is_keyword(ptr, len, "alloc")))
+                               uc->data |= DEBUG_ALLOC;
+                       else if ((arg_len = is_keyword(ptr, len, "phase")))
+                               uc->data |= DEBUG_PHASE;
+                       else if ((arg_len = is_keyword(ptr, len, "queue")))
+                               uc->data |= DEBUG_QUEUE;
+                       else if ((arg_len = is_keyword(ptr, len, "result")))
+                               uc->data |= DEBUG_RESULT;
+                       else if ((arg_len = is_keyword(ptr, len, "scatter")))
+                               uc->data |= DEBUG_SCATTER;
+                       else if ((arg_len = is_keyword(ptr, len, "script")))
+                               uc->data |= DEBUG_SCRIPT;
+                       else if ((arg_len = is_keyword(ptr, len, "tiny")))
+                               uc->data |= DEBUG_TINY;
+                       else if ((arg_len = is_keyword(ptr, len, "timing")))
+                               uc->data |= DEBUG_TIMING;
+                       else if ((arg_len = is_keyword(ptr, len, "nego")))
+                               uc->data |= DEBUG_NEGO;
+                       else if ((arg_len = is_keyword(ptr, len, "tags")))
+                               uc->data |= DEBUG_TAGS;
+                       else if ((arg_len = is_keyword(ptr, len, "pointer")))
+                               uc->data |= DEBUG_POINTER;
+                       else
+                               return -EINVAL;
+                       ptr += arg_len; len -= arg_len;
+               }
+#ifdef DEBUG_PROC_INFO
+printk("sym_user_command: data=%ld\n", uc->data);
+#endif
+               break;
+#endif /* SYM_LINUX_DEBUG_CONTROL_SUPPORT */
+       case UC_SETFLAG:
+               while (len > 0) {
+                       SKIP_SPACES(1);
+                       if      ((arg_len = is_keyword(ptr, len, "no_disc")))
+                               uc->data &= ~SYM_DISC_ENABLED;
+                       else
+                               return -EINVAL;
+                       ptr += arg_len; len -= arg_len;
+               }
+               break;
+       default:
+               break;
+       }
+
+       if (len)
+               return -EINVAL;
+       else {
+               long flags;
+
+               SYM_LOCK_HCB(np, flags);
+               sym_exec_user_command (np, uc);
+               SYM_UNLOCK_HCB(np, flags);
+       }
+       return length;
+}
+
+#endif /* SYM_LINUX_USER_COMMAND_SUPPORT */
+
+
+#ifdef SYM_LINUX_USER_INFO_SUPPORT
+/*
+ *  Informations through the proc file system.
+ */
+struct info_str {
+       char *buffer;
+       int length;
+       int offset;
+       int pos;
+};
+
+static void copy_mem_info(struct info_str *info, char *data, int len)
+{
+       if (info->pos + len > info->length)
+               len = info->length - info->pos;
+
+       if (info->pos + len < info->offset) {
+               info->pos += len;
+               return;
+       }
+       if (info->pos < info->offset) {
+               data += (info->offset - info->pos);
+               len  -= (info->offset - info->pos);
+       }
+
+       if (len > 0) {
+               memcpy(info->buffer + info->pos, data, len);
+               info->pos += len;
+       }
+}
+
+static int copy_info(struct info_str *info, char *fmt, ...)
+{
+       va_list args;
+       char buf[81];
+       int len;
+
+       va_start(args, fmt);
+       len = vsprintf(buf, fmt, args);
+       va_end(args);
+
+       copy_mem_info(info, buf, len);
+       return len;
+}
+
+/*
+ *  Copy formatted information into the input buffer.
+ */
+/* SAE: Again with the off_t */
+#ifndef off_t
+#define off_t int
+#endif
+static int sym_host_info(hcb_p np, char *ptr, off_t offset, int len)
+{
+       struct info_str info;
+
+       info.buffer     = ptr;
+       info.length     = len;
+       info.offset     = offset;
+       info.pos        = 0;
+
+       copy_info(&info, "Chip " NAME53C "%s, device id 0x%x, "
+                        "revision id 0x%x\n",
+                        np->s.chip_name, np->device_id, np->revision_id);
+       copy_info(&info, "On PCI bus %d, device %d, function %d, "
+#ifdef __sparc__
+               "IRQ %s\n",
+#else
+               "IRQ %d\n",
+#endif
+               np->s.bus, (np->s.device_fn & 0xf8) >> 3, np->s.device_fn & 7,
+#ifdef __sparc__
+               __irq_itoa(np->s.irq));
+#else
+               (int) np->s.irq);
+#endif
+       copy_info(&info, "Min. period factor %d, %s SCSI BUS%s\n",
+                        (int) (np->minsync_dt ? np->minsync_dt : np->minsync),
+                        np->maxwide ? "Wide" : "Narrow",
+                        np->minsync_dt ? ", DT capable" : "");
+
+       copy_info(&info, "Max. started commands %d, "
+                        "max. commands per LUN %d\n",
+                        SYM_CONF_MAX_START, SYM_CONF_MAX_TAG);
+
+       return info.pos > info.offset? info.pos - info.offset : 0;
+}
+#endif /* SYM_LINUX_USER_INFO_SUPPORT */
+
+/*
+ *  Entry point of the scsi proc fs of the driver.
+ *  - func = 0 means read  (returns adapter infos)
+ *  - func = 1 means write (not yet merget from sym53c8xx)
+ */
+static int sym53c8xx_proc_info(char *buffer, char **start, off_t offset,
+                       int length, int hostno, int func)
+{
+       struct Scsi_Host *host;
+       struct host_data *host_data;
+       hcb_p np = 0;
+       int retv;
+
+       for (host = first_host; host; host = host->next) {
+               if (host->hostt != first_host->hostt)
+                       continue;
+               if (host->host_no == hostno) {
+                       host_data = (struct host_data *) host->hostdata;
+                       np = host_data->ncb;
+                       break;
+               }
+       }
+
+       if (!np)
+               return -EINVAL;
+
+       if (func) {
+#ifdef SYM_LINUX_USER_COMMAND_SUPPORT
+               retv = sym_user_command(np, buffer, length);
+#else
+               retv = -EINVAL;
+#endif
+       }
+       else {
+               if (start)
+                       *start = buffer;
+#ifdef SYM_LINUX_USER_INFO_SUPPORT
+               retv = sym_host_info(np, buffer, offset, length);
+#else
+               retv = -EINVAL;
+#endif
+       }
+
+       return retv;
+}
+#endif /* SYM_LINUX_PROC_INFO_SUPPORT */
+
+/*
+ *     Free controller resources.
+ */
+static void sym_free_resources(hcb_p np)
+{
+       /*
+        *  Free O/S specific resources.
+        */
+       if (np->s.irq)
+               free_irq(np->s.irq, np);
+       if (np->s.io_port)
+               release_region(np->s.io_port, np->s.io_ws);
+#ifndef SYM_OPT_NO_BUS_MEMORY_MAPPING
+       if (np->s.mmio_va)
+               pci_unmap_mem(np->s.mmio_va, np->s.io_ws);
+       if (np->s.ram_va)
+               pci_unmap_mem(np->s.ram_va, np->ram_ws);
+#endif
+       /*
+        *  Free O/S independant resources.
+        */
+       sym_hcb_free(np);
+
+       sym_mfree_dma(np, sizeof(*np), "HCB");
+}
+
+/*
+ *  Ask/tell the system about DMA addressing.
+ */
+#ifdef SYM_LINUX_DYNAMIC_DMA_MAPPING
+static int sym_setup_bus_dma_mask(hcb_p np)
+{
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,4,3)
+       if (!pci_dma_supported(np->s.device, 0xffffffffUL))
+               goto out_err32;
+#else
+#if   SYM_CONF_DMA_ADDRESSING_MODE == 0
+       if (pci_set_dma_mask(np->s.device, 0xffffffffUL))
+               goto out_err32;
+#else
+#if   SYM_CONF_DMA_ADDRESSING_MODE == 1
+#define        PciDmaMask      0xffffffffff
+#elif SYM_CONF_DMA_ADDRESSING_MODE == 2
+#define        PciDmaMask      0xffffffffffffffff
+#endif
+       if (np->features & FE_DAC) {
+               if (!pci_set_dma_mask(np->s.device, PciDmaMask)) {
+                       np->use_dac = 1;
+                       printf_info("%s: using 64 bit DMA addressing\n",
+                                       sym_name(np));
+               }
+               else {
+                       if (pci_set_dma_mask(np->s.device, 0xffffffffUL))
+                               goto out_err32;
+               }
+       }
+#undef PciDmaMask
+#endif
+#endif
+       return 0;
+
+out_err32:
+       printf_warning("%s: 32 BIT DMA ADDRESSING NOT SUPPORTED\n",
+                       sym_name(np));
+       return -1;
+}
+#endif /* SYM_LINUX_DYNAMIC_DMA_MAPPING */
+
+/*
+ *  Host attach and initialisations.
+ *
+ *  Allocate host data and ncb structure.
+ *  Request IO region and remap MMIO region.
+ *  Do chip initialization.
+ *  If all is OK, install interrupt handling and
+ *  start the timer daemon.
+ */
+static int __init 
+sym_attach (Scsi_Host_Template *tpnt, int unit, sym_device *dev)
+{
+        struct host_data *host_data;
+       hcb_p np = 0;
+        struct Scsi_Host *instance = 0;
+       u_long flags = 0;
+       sym_nvram *nvram = dev->nvram;
+       struct sym_fw *fw;
+
+       printk(KERN_INFO
+               "sym%d: <%s> rev 0x%x on pci bus %d device %d function %d "
+               "irq %d\n",
+               unit, dev->chip.name, dev->chip.revision_id,
+               dev->s.bus, (dev->s.device_fn & 0xf8) >> 3,
+               dev->s.device_fn & 7,
+               dev->s.irq);
+
+/* SAE: No sparc in Xen... */
+#ifdef XENO_KILLED
+#ifdef __sparc__
+               "irq %s\n",
+#else
+               "irq %d\n",
+#endif
+               unit, dev->chip.name, dev->chip.revision_id,
+               dev->s.bus, (dev->s.device_fn & 0xf8) >> 3,
+               dev->s.device_fn & 7,
+#ifdef __sparc__
+               __irq_itoa(dev->s.irq));
+#else
+               dev->s.irq);
+#endif
+#endif /* XENO_KILLED */
+
+       /*
+        *  Get the firmware for this chip.
+        */
+       fw = sym_find_firmware(&dev->chip);
+       if (!fw)
+               goto attach_failed;
+
+       /*
+        *      Allocate host_data structure
+        */
+        if (!(instance = scsi_register(tpnt, sizeof(*host_data))))
+               goto attach_failed;
+       host_data = (struct host_data *) instance->hostdata;
+
+       /*
+        *  Allocate immediately the host control block, 
+        *  since we are only expecting to succeed. :)
+        *  We keep track in the HCB of all the resources that 
+        *  are to be released on error.
+        */
+#ifdef SYM_LINUX_DYNAMIC_DMA_MAPPING
+       np = __sym_calloc_dma(dev->pdev, sizeof(*np), "HCB");
+       if (np) {
+               np->s.device = dev->pdev;
+               np->bus_dmat = dev->pdev; /* Result in 1 DMA pool per HBA */
+       }
+       else
+               goto attach_failed;
+#else
+       np = sym_calloc_dma(sizeof(*np), "HCB");
+       if (!np)
+               goto attach_failed;
+#endif
+       host_data->ncb = np;
+
+       SYM_INIT_LOCK_HCB(np);
+
+       /*
+        *  Copy some useful infos to the HCB.
+        */
+       np->hcb_ba      = vtobus(np);
+       np->verbose     = sym_driver_setup.verbose;
+       np->s.device    = dev->pdev;
+       np->s.unit      = unit;
+       np->device_id   = dev->chip.device_id;
+       np->revision_id = dev->chip.revision_id;
+       np->s.bus       = dev->s.bus;
+       np->s.device_fn = dev->s.device_fn;
+       np->features    = dev->chip.features;
+       np->clock_divn  = dev->chip.nr_divisor;
+       np->maxoffs     = dev->chip.offset_max;
+       np->maxburst    = dev->chip.burst_max;
+       np->myaddr      = dev->host_id;
+
+       /*
+        *  Edit its name.
+        */
+       strncpy(np->s.chip_name, dev->chip.name, sizeof(np->s.chip_name)-1);
+       sprintf(np->s.inst_name, "sym%d", np->s.unit);
+
+       /*
+        *  Ask/tell the system about DMA addressing.
+        */
+#ifdef SYM_LINUX_DYNAMIC_DMA_MAPPING
+       if (sym_setup_bus_dma_mask(np))
+               goto attach_failed;
+#endif
+
+       /*
+        *  Try to map the controller chip to
+        *  virtual and physical memory.
+        */
+       np->mmio_ba     = (u32)dev->s.base;
+       np->s.io_ws     = (np->features & FE_IO256)? 256 : 128;
+
+#ifndef SYM_CONF_IOMAPPED
+       np->s.mmio_va = pci_map_mem(dev->s.base_c, np->s.io_ws);
+       if (!np->s.mmio_va) {
+               printf_err("%s: can't map PCI MMIO region\n", sym_name(np));
+               goto attach_failed;
+       }
+       else if (sym_verbose > 1)
+               printf_info("%s: using memory mapped IO\n", sym_name(np));
+#endif /* !defined SYM_CONF_IOMAPPED */
+
+       /*
+        *  Try to map the controller chip into iospace.
+        */
+       if (dev->s.io_port) {
+               request_region(dev->s.io_port, np->s.io_ws, NAME53C8XX);
+               np->s.io_port = dev->s.io_port;
+       }
+
+       /*
+        *  Map on-chip RAM if present and supported.
+        */
+       if (!(np->features & FE_RAM))
+               dev->s.base_2 = 0;
+       if (dev->s.base_2) {
+               np->ram_ba = (u32)dev->s.base_2;
+               if (np->features & FE_RAM8K)
+                       np->ram_ws = 8192;
+               else
+                       np->ram_ws = 4096;
+#ifndef SYM_OPT_NO_BUS_MEMORY_MAPPING
+               np->s.ram_va = pci_map_mem(dev->s.base_2_c, np->ram_ws);
+               if (!np->s.ram_va) {
+                       printf_err("%s: can't map PCI MEMORY region\n",
+                              sym_name(np));
+                       goto attach_failed;
+               }
+#endif
+       }
+
+       /*
+        *  Perform O/S independant stuff.
+        */
+       if (sym_hcb_attach(np, fw, nvram))
+               goto attach_failed;
+
+
+       /*
+        *  Install the interrupt handler.
+        *  If we synchonize the C code with SCRIPTS on interrupt, 
+        *  we donnot want to share the INTR line at all.
+        */
+       if (request_irq(dev->s.irq, sym53c8xx_intr, SA_SHIRQ,
+                       NAME53C8XX, np)) {
+               printf_err("%s: request irq %d failure\n",
+                       sym_name(np), dev->s.irq);
+               goto attach_failed;
+       }
+       np->s.irq = dev->s.irq;
+
+       /*
+        *  After SCSI devices have been opened, we cannot
+        *  reset the bus safely, so we do it here.
+        */
+       SYM_LOCK_HCB(np, flags);
+       if (sym_reset_scsi_bus(np, 0)) {
+               printf_err("%s: FATAL ERROR: CHECK SCSI BUS - CABLES, "
+                          "TERMINATION, DEVICE POWER etc.!\n", sym_name(np));
+               SYM_UNLOCK_HCB(np, flags);
+               goto attach_failed;
+       }
+
+       /*
+        *  Initialize some queue headers.
+        */
+       sym_que_init(&np->s.wait_cmdq);
+       sym_que_init(&np->s.busy_cmdq);
+
+       /*
+        *  Start the SCRIPTS.
+        */
+       sym_start_up (np, 1);
+
+       /*
+        *  Start the timer daemon
+        */
+       init_timer(&np->s.timer);
+       np->s.timer.data     = (unsigned long) np;
+       np->s.timer.function = sym53c8xx_timer;
+       np->s.lasttime=0;
+       sym_timer (np);
+
+       /*
+        *  Done.
+        */
+        if (!first_host)
+               first_host = instance;
+
+       /*
+        *  Fill Linux host instance structure
+        *  and return success.
+        */
+       instance->max_channel   = 0;
+       instance->this_id       = np->myaddr;
+       instance->max_id        = np->maxwide ? 16 : 8;
+       instance->max_lun       = SYM_CONF_MAX_LUN;
+#ifndef SYM_CONF_IOMAPPED
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,29)
+       instance->base          = (unsigned long) np->s.mmio_va;
+#else
+       instance->base          = (char *) np->s.mmio_va;
+#endif
+#endif
+       instance->irq           = np->s.irq;
+       instance->unique_id     = np->s.io_port;
+       instance->io_port       = np->s.io_port;
+       instance->n_io_port     = np->s.io_ws;
+       instance->dma_channel   = 0;
+       instance->cmd_per_lun   = SYM_CONF_MAX_TAG;
+       instance->can_queue     = (SYM_CONF_MAX_START-2);
+       instance->sg_tablesize  = SYM_CONF_MAX_SG;
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
+       instance->max_cmd_len   = 16;
+#endif
+       instance->select_queue_depths = sym53c8xx_select_queue_depths;
+/* SAE */
+#ifdef XENO_KILLED
+       instance->highmem_io    = 1;
+#endif
+
+       SYM_UNLOCK_HCB(np, flags);
+
+       scsi_set_pci_device(instance, dev->pdev);
+
+       /*
+        *  Now let the generic SCSI driver
+        *  look for the SCSI devices on the bus ..
+        */
+       return 0;
+
+attach_failed:
+       if (!instance) return -1;
+       printf_info("%s: giving up ...\n", sym_name(np));
+       if (np)
+               sym_free_resources(np);
+       scsi_unregister(instance);
+
+        return -1;
+ }
+
+
+/*
+ *    Detect and try to read SYMBIOS and TEKRAM NVRAM.
+ */
+#if SYM_CONF_NVRAM_SUPPORT
+static void __init sym_get_nvram(sym_device *devp, sym_nvram *nvp)
+{
+       if (!nvp)
+               return;
+
+       devp->nvram = nvp;
+       devp->device_id = devp->chip.device_id;
+       nvp->type = 0;
+
+       /*
+        *  Get access to chip IO registers
+        */
+#ifdef SYM_CONF_IOMAPPED
+       request_region(devp->s.io_port, 128, NAME53C8XX);
+#else
+       devp->s.mmio_va = pci_map_mem(devp->s.base_c, 128);
+       if (!devp->s.mmio_va)
+               return;
+#endif
+
+       /*
+        *  Try to read SYMBIOS|TEKRAM nvram.
+        */
+       (void) sym_read_nvram(devp, nvp);
+
+       /*
+        *  Release access to chip IO registers
+        */
+#ifdef SYM_CONF_IOMAPPED
+       release_region(devp->s.io_port, 128);
+#else
+       pci_unmap_mem((u_long) devp->s.mmio_va, 128ul);
+#endif
+}
+#endif /* SYM_CONF_NVRAM_SUPPORT */
+
+/*
+ *  Driver setup from the boot command line
+ */
+#ifdef SYM_LINUX_BOOT_COMMAND_LINE_SUPPORT
+
+static struct sym_driver_setup
+       sym_driver_safe_setup __initdata = SYM_LINUX_DRIVER_SAFE_SETUP;
+#ifdef MODULE
+char *sym53c8xx = 0;   /* command line passed by insmod */
+MODULE_PARM(sym53c8xx, "s");
+#endif
+
+static void __init sym53c8xx_print_driver_setup(void)
+{
+       printf_info (NAME53C8XX ": setup="
+               "mpar:%d,spar:%d,tags:%d,sync:%d,burst:%d,"
+               "led:%d,wide:%d,diff:%d,irqm:%d, buschk:%d\n",
+               sym_driver_setup.pci_parity,
+               sym_driver_setup.scsi_parity,
+               sym_driver_setup.max_tag,
+               sym_driver_setup.min_sync,
+               sym_driver_setup.burst_order,
+               sym_driver_setup.scsi_led,
+               sym_driver_setup.max_wide,
+               sym_driver_setup.scsi_diff,
+               sym_driver_setup.irq_mode,
+               sym_driver_setup.scsi_bus_check);
+       printf_info (NAME53C8XX ": setup="
+               "hostid:%d,offs:%d,luns:%d,pcifix:%d,revprob:%d,"
+               "verb:%d,debug:0x%x,setlle_delay:%d\n",
+               sym_driver_setup.host_id,
+               sym_driver_setup.max_offs,
+               sym_driver_setup.max_lun,
+               sym_driver_setup.pci_fix_up,
+               sym_driver_setup.reverse_probe,
+               sym_driver_setup.verbose,
+               sym_driver_setup.debug,
+               sym_driver_setup.settle_delay);
+#ifdef DEBUG_2_0_X
+MDELAY(5000);
+#endif
+};
+
+#define OPT_PCI_PARITY         1
+#define        OPT_SCSI_PARITY         2
+#define OPT_MAX_TAG            3
+#define OPT_MIN_SYNC           4
+#define OPT_BURST_ORDER                5
+#define OPT_SCSI_LED           6
+#define OPT_MAX_WIDE           7
+#define OPT_SCSI_DIFF          8
+#define OPT_IRQ_MODE           9
+#define OPT_SCSI_BUS_CHECK     10
+#define        OPT_HOST_ID             11
+#define OPT_MAX_OFFS           12
+#define OPT_MAX_LUN            13
+#define OPT_PCI_FIX_UP         14
+
+#define OPT_REVERSE_PROBE      15
+#define OPT_VERBOSE            16
+#define OPT_DEBUG              17
+#define OPT_SETTLE_DELAY       18
+#define OPT_USE_NVRAM          19
+#define OPT_EXCLUDE            20
+#define OPT_SAFE_SETUP         21
+
+static char setup_token[] __initdata =
+       "mpar:"         "spar:"
+       "tags:"         "sync:"
+       "burst:"        "led:"
+       "wide:"         "diff:"
+       "irqm:"         "buschk:"
+       "hostid:"       "offset:"
+       "luns:"         "pcifix:"
+       "revprob:"      "verb:"
+       "debug:"        "settle:"
+       "nvram:"        "excl:"
+       "safe:"
+       ;
+
+#ifdef MODULE
+#define        ARG_SEP ' '
+#else
+#define        ARG_SEP ','
+#endif
+
+static int __init get_setup_token(char *p)
+{
+       char *cur = setup_token;
+       char *pc;
+       int i = 0;
+
+       while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+               ++pc;
+               ++i;
+               if (!strncmp(p, cur, pc - cur))
+                       return i;
+               cur = pc;
+       }
+       return 0;
+}
+#endif /* SYM_LINUX_BOOT_COMMAND_LINE_SUPPORT */
+
+int __init sym53c8xx_setup(char *str)
+{
+#ifdef SYM_LINUX_BOOT_COMMAND_LINE_SUPPORT
+       char *cur = str;
+       char *pc, *pv;
+       unsigned long val;
+       int i,  c;
+       int xi = 0;
+
+       while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+               char *pe;
+
+               val = 0;
+               pv = pc;
+               c = *++pv;
+
+               if      (c == 'n')
+                       val = 0;
+               else if (c == 'y')
+                       val = 1;
+               else
+                       val = (int) simple_strtoul(pv, &pe, 0);
+
+               switch (get_setup_token(cur)) {
+               case OPT_MAX_TAG:
+                       sym_driver_setup.max_tag = val;
+                       if (!(pe && *pe == '/'))
+                               break;
+                       i = 0;
+                       while (*pe && *pe != ARG_SEP && 
+                               i < sizeof(sym_driver_setup.tag_ctrl)-1) {
+                               sym_driver_setup.tag_ctrl[i++] = *pe++;
+                       }
+                       sym_driver_setup.tag_ctrl[i] = '\0';
+                       break;
+               case OPT_SAFE_SETUP:
+                       memcpy(&sym_driver_setup, &sym_driver_safe_setup,
+                               sizeof(sym_driver_setup));
+                       break;
+               case OPT_EXCLUDE:
+                       if (xi < 8)
+                               sym_driver_setup.excludes[xi++] = val;
+                       break;
+
+#define __SIMPLE_OPTION(NAME, name) \
+               case OPT_ ## NAME :             \
+                       sym_driver_setup.name = val;\
+                       break;
+
+               __SIMPLE_OPTION(PCI_PARITY, pci_parity)
+               __SIMPLE_OPTION(SCSI_PARITY, scsi_parity)
+               __SIMPLE_OPTION(MIN_SYNC, min_sync)
+               __SIMPLE_OPTION(BURST_ORDER, burst_order)
+               __SIMPLE_OPTION(SCSI_LED, scsi_led)
+               __SIMPLE_OPTION(MAX_WIDE, max_wide)
+               __SIMPLE_OPTION(SCSI_DIFF, scsi_diff)
+               __SIMPLE_OPTION(IRQ_MODE, irq_mode)
+               __SIMPLE_OPTION(SCSI_BUS_CHECK, scsi_bus_check)
+               __SIMPLE_OPTION(HOST_ID, host_id)
+               __SIMPLE_OPTION(MAX_OFFS, max_offs)
+               __SIMPLE_OPTION(MAX_LUN, max_lun)
+               __SIMPLE_OPTION(PCI_FIX_UP, pci_fix_up)
+               __SIMPLE_OPTION(REVERSE_PROBE, reverse_probe)
+               __SIMPLE_OPTION(VERBOSE, verbose)
+               __SIMPLE_OPTION(DEBUG, debug)
+               __SIMPLE_OPTION(SETTLE_DELAY, settle_delay)
+               __SIMPLE_OPTION(USE_NVRAM, use_nvram)
+
+#undef __SIMPLE_OPTION
+
+               default:
+                       printk("sym53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
+                       break;
+               }
+
+               if ((cur = strchr(cur, ARG_SEP)) != NULL)
+                       ++cur;
+       }
+#endif /* SYM_LINUX_BOOT_COMMAND_LINE_SUPPORT */
+       return 1;
+}
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+#ifndef MODULE
+__setup("sym53c8xx=", sym53c8xx_setup);
+#endif
+#endif
+
+#ifdef SYM_CONF_PQS_PDS_SUPPORT
+/*
+ *  Detect all NCR PQS/PDS boards and keep track of their bus nr.
+ *
+ *  The NCR PQS or PDS card is constructed as a DEC bridge
+ *  behind which sit a proprietary NCR memory controller and
+ *  four or two 53c875s as separate devices.  In its usual mode
+ *  of operation, the 875s are slaved to the memory controller
+ *  for all transfers.  We can tell if an 875 is part of a
+ *  PQS/PDS or not since if it is, it will be on the same bus
+ *  as the memory controller.  To operate with the Linux
+ *  driver, the memory controller is disabled and the 875s
+ *  freed to function independently.  The only wrinkle is that
+ *  the preset SCSI ID (which may be zero) must be read in from
+ *  a special configuration space register of the 875
+ */
+#ifndef SYM_CONF_MAX_PQS_BUS
+#define SYM_CONF_MAX_PQS_BUS 16
+#endif
+static int pqs_bus[SYM_CONF_MAX_PQS_BUS] __initdata = { 0 };
+
+static void __init sym_detect_pqs_pds(void)
+{
+       short index;
+       pcidev_t dev = PCIDEV_NULL;
+
+       for(index=0; index < SYM_CONF_MAX_PQS_BUS; index++) {
+               u_char tmp;
+
+               dev = pci_find_device(0x101a, 0x0009, dev);
+               if (dev == PCIDEV_NULL) {
+                       pqs_bus[index] = -1;
+                       break;
+               }
+               printf_info(NAME53C8XX ": NCR PQS/PDS memory controller detected on bus %d\n", PciBusNumber(dev));
+               pci_read_config_byte(dev, 0x44, &tmp);
+               /* bit 1: allow individual 875 configuration */
+               tmp |= 0x2;
+               pci_write_config_byte(dev, 0x44, tmp);
+               pci_read_config_byte(dev, 0x45, &tmp);
+               /* bit 2: drive individual 875 interrupts to the bus */
+               tmp |= 0x4;
+               pci_write_config_byte(dev, 0x45, tmp);
+
+               pqs_bus[index] = PciBusNumber(dev);
+       }
+}
+#endif /* SYM_CONF_PQS_PDS_SUPPORT */
+
+/*
+ *  Read and check the PCI configuration for any detected NCR 
+ *  boards and save data for attaching after all boards have 
+ *  been detected.
+ */
+static int __init
+sym53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, sym_device *device)
+{
+       u_short vendor_id, device_id, command, status_reg;
+       u_char cache_line_size;
+       u_char suggested_cache_line_size = 0;
+       u_char pci_fix_up = SYM_SETUP_PCI_FIX_UP;
+       u_char revision;
+       u_int irq;
+       u_long base, base_2, base_io; 
+       u_long base_c, base_2_c, io_port; 
+       int i;
+       sym_chip *chip;
+
+       /* Choose some short name for this device */
+       sprintf(device->s.inst_name, "sym.%d.%d.%d",
+               PciBusNumber(pdev),
+               (int) (PciDeviceFn(pdev) & 0xf8) >> 3,
+               (int) (PciDeviceFn(pdev) & 7));
+
+       /*
+        *  Read needed minimal info from the PCI config space.
+        */
+       vendor_id = PciVendorId(pdev);
+       device_id = PciDeviceId(pdev);
+       irq       = PciIrqLine(pdev);
+
+       i = pci_get_base_address(pdev, 0, &base_io);
+       io_port = pci_get_base_cookie(pdev, 0);
+
+       base_c = pci_get_base_cookie(pdev, i);
+       i = pci_get_base_address(pdev, i, &base);
+
+       base_2_c = pci_get_base_cookie(pdev, i);
+       (void) pci_get_base_address(pdev, i, &base_2);
+
+       io_port &= PCI_BASE_ADDRESS_IO_MASK;
+       base    &= PCI_BASE_ADDRESS_MEM_MASK;
+       base_2  &= PCI_BASE_ADDRESS_MEM_MASK;
+
+       pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
+
+       /*
+        *  If user excluded this chip, donnot initialize it.
+        */
+       if (base_io) {
+               for (i = 0 ; i < 8 ; i++) {
+                       if (sym_driver_setup.excludes[i] == base_io)
+                               return -1;
+               }
+       }
+
+       /*
+        *  Leave here if another driver attached the chip.
+        */
+       if (io_port && check_region (io_port, 128)) {
+               printf_info("%s: IO region 0x%lx[0..127] is in use\n",
+                           sym_name(device), (long) io_port);
+               return -1;
+       }
+
+       /*
+        *  Check if the chip is supported.
+        */
+       chip = sym_lookup_pci_chip_table(device_id, revision);
+       if (!chip) {
+               printf_info("%s: device not supported\n", sym_name(device));
+               return -1;
+       }
+
+       /*
+        *  Check if the chip has been assigned resources we need.
+        */
+#ifdef SYM_CONF_IOMAPPED
+       if (!io_port) {
+               printf_info("%s: IO base address disabled.\n",
+                           sym_name(device));
+               return -1;
+       }
+#else
+       if (!base) {
+               printf_info("%s: MMIO base address disabled.\n",
+                           sym_name(device));
+               return -1;
+       }
+#endif
+
+       /*
+        *  Ignore Symbios chips controlled by various RAID controllers.
+        *  These controllers set value 0x52414944 at RAM end - 16.
+        */
+#if defined(__i386__) && !defined(SYM_OPT_NO_BUS_MEMORY_MAPPING)
+       if (base_2_c) {
+               unsigned int ram_size, ram_val;
+               u_long ram_ptr;
+
+               if (chip->features & FE_RAM8K)
+                       ram_size = 8192;
+               else
+                       ram_size = 4096;
+
+               ram_ptr = pci_map_mem(base_2_c, ram_size);
+               if (ram_ptr) {
+                       ram_val = readl_raw(ram_ptr + ram_size - 16);
+                       pci_unmap_mem(ram_ptr, ram_size);
+                       if (ram_val == 0x52414944) {
+                               printf_info("%s: not initializing, "
+                                           "driven by RAID controller.\n",
+                                           sym_name(device));
+                               return -1;
+                       }
+               }
+       }
+#endif /* i386 and PCI MEMORY accessible */
+
+       /*
+        *  Copy the chip description to our device structure, 
+        *  so we can make it match the actual device and options.
+        */
+       bcopy(chip, &device->chip, sizeof(device->chip));
+       device->chip.revision_id = revision;
+
+       /*
+        *  Read additionnal info from the configuration space.
+        */
+       pci_read_config_word(pdev, PCI_COMMAND,         &command);
+       pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size);
+
+       /*
+        * Enable missing capabilities in the PCI COMMAND register.
+        */
+#ifdef SYM_CONF_IOMAPPED
+#define        PCI_COMMAND_BITS_TO_ENABLE (PCI_COMMAND_IO | \
+       PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_PARITY)
+#else
+#define        PCI_COMMAND_BITS_TO_ENABLE \
+       (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_PARITY)
+#endif
+       if ((command & PCI_COMMAND_BITS_TO_ENABLE)
+                   != PCI_COMMAND_BITS_TO_ENABLE) {
+               printf_info("%s: setting%s%s%s%s...\n", sym_name(device),
+               (command & PCI_COMMAND_IO)     ? "" : " PCI_COMMAND_IO",
+               (command & PCI_COMMAND_MEMORY) ? "" : " PCI_COMMAND_MEMORY",
+               (command & PCI_COMMAND_MASTER) ? "" : " PCI_COMMAND_MASTER",
+               (command & PCI_COMMAND_PARITY) ? "" : " PCI_COMMAND_PARITY");
+               command |= PCI_COMMAND_BITS_TO_ENABLE;
+               pci_write_config_word(pdev, PCI_COMMAND, command);
+       }
+#undef PCI_COMMAND_BITS_TO_ENABLE
+
+       /*
+        *  If cache line size is not configured, suggest
+        *  a value for well known CPUs.
+        */
+#if defined(__i386__) && !defined(MODULE)
+       if (!cache_line_size && boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) {
+               switch(boot_cpu_data.x86) {
+               case 4: suggested_cache_line_size = 4;   break;
+               case 6: if (boot_cpu_data.x86_model > 8) break;
+               case 5: suggested_cache_line_size = 8;   break;
+               }
+       }
+#endif /* __i386__ */
+
+       /*
+        *  Some features are required to be enabled in order to 
+        *  work around some chip problems. :) ;)
+        *  (ITEM 12 of a DEL about the 896 I haven't yet).
+        *  We must ensure the chip will use WRITE AND INVALIDATE.
+        *  The revision number limit is for now arbitrary.
+        */
+       if (device_id == PCI_DEVICE_ID_NCR_53C896 && revision < 0x4) {
+               chip->features  |= (FE_WRIE | FE_CLSE);
+               pci_fix_up      |=  3;  /* Force appropriate PCI fix-up */
+       }
+
+#ifdef SYM_CONF_PCI_FIX_UP
+       /*
+        *  Try to fix up PCI config according to wished features.
+        */
+       if ((pci_fix_up & 1) && (chip->features & FE_CLSE) && 
+           !cache_line_size && suggested_cache_line_size) {
+               cache_line_size = suggested_cache_line_size;
+               pci_write_config_byte(pdev,
+                                     PCI_CACHE_LINE_SIZE, cache_line_size);
+               printf_info("%s: PCI_CACHE_LINE_SIZE set to %d.\n",
+                           sym_name(device), cache_line_size);
+       }
+
+       if ((pci_fix_up & 2) && cache_line_size &&
+           (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+               printf_info("%s: setting PCI_COMMAND_INVALIDATE.\n",
+                           sym_name(device));
+               command |= PCI_COMMAND_INVALIDATE;
+               pci_write_config_word(pdev, PCI_COMMAND, command);
+       }
+#endif /* SYM_CONF_PCI_FIX_UP */
+
+       /*
+        *  Work around for errant bit in 895A. The 66Mhz
+        *  capable bit is set erroneously. Clear this bit.
+        *  (Item 1 DEL 533)
+        *
+        *  Make sure Config space and Features agree.
+        *
+        *  Recall: writes are not normal to status register -
+        *  write a 1 to clear and a 0 to leave unchanged.
+        *  Can only reset bits.
+        */
+       pci_read_config_word(pdev, PCI_STATUS, &status_reg);
+       if (chip->features & FE_66MHZ) {
+               if (!(status_reg & PCI_STATUS_66MHZ))
+                       chip->features &= ~FE_66MHZ;
+       }
+       else {
+               if (status_reg & PCI_STATUS_66MHZ) {
+                       status_reg = PCI_STATUS_66MHZ;
+                       pci_write_config_word(pdev, PCI_STATUS, status_reg);
+                       pci_read_config_word(pdev, PCI_STATUS, &status_reg);
+               }
+       }
+
+       /*
+        *  Initialise device structure with items required by sym_attach.
+        */
+       device->pdev            = pdev;
+       device->s.bus           = PciBusNumber(pdev);
+       device->s.device_fn     = PciDeviceFn(pdev);
+       device->s.base          = base;
+       device->s.base_2        = base_2;
+       device->s.base_c        = base_c;
+       device->s.base_2_c      = base_2_c;
+       device->s.io_port       = io_port;
+       device->s.irq           = irq;
+       device->attach_done     = 0;
+
+       return 0;
+}
+
+/*
+ *  List of supported NCR chip ids
+ */
+static u_short sym_chip_ids[] __initdata       = {
+       PCI_ID_SYM53C810,
+       PCI_ID_SYM53C815,
+       PCI_ID_SYM53C825,
+       PCI_ID_SYM53C860,
+       PCI_ID_SYM53C875,
+       PCI_ID_SYM53C875_2,
+       PCI_ID_SYM53C885,
+       PCI_ID_SYM53C875A,
+       PCI_ID_SYM53C895,
+       PCI_ID_SYM53C896,
+       PCI_ID_SYM53C895A,
+       PCI_ID_LSI53C1510D,
+       PCI_ID_LSI53C1010,
+       PCI_ID_LSI53C1010_2
+};
+
+/*
+ *  Detect all 53c8xx hosts and then attach them.
+ *
+ *  If we are using NVRAM, once all hosts are detected, we need to 
+ *  check any NVRAM for boot order in case detect and boot order 
+ *  differ and attach them using the order in the NVRAM.
+ *
+ *  If no NVRAM is found or data appears invalid attach boards in 
+ *  the order they are detected.
+ */
+int __init sym53c8xx_detect(Scsi_Host_Template *tpnt)
+{
+       pcidev_t pcidev;
+       int i, j, chips, hosts, count;
+       int attach_count = 0;
+       sym_device *devtbl, *devp;
+       sym_nvram  nvram;
+#if SYM_CONF_NVRAM_SUPPORT
+       sym_nvram  nvram0, *nvp;
+#endif
+
+       /*
+        *  PCI is required.
+        */
+       if (!pci_present())
+               return 0;
+
+       /*
+        *    Initialize driver general stuff.
+        */
+/* SAE: No proc */
+#ifdef XENO_KILLED
+#ifdef SYM_LINUX_PROC_INFO_SUPPORT
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+     tpnt->proc_dir  = &proc_scsi_sym53c8xx;
+#else
+     tpnt->proc_name = NAME53C8XX;
+#endif
+     tpnt->proc_info = sym53c8xx_proc_info;
+#endif
+#endif /* XENO_KILLED */
+
+#ifdef SYM_LINUX_BOOT_COMMAND_LINE_SUPPORT
+#ifdef MODULE
+if (sym53c8xx)
+       sym53c8xx_setup(sym53c8xx);
+#endif
+#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT
+       sym_debug_flags = sym_driver_setup.debug;
+#endif
+       if (boot_verbose >= 2)
+               sym53c8xx_print_driver_setup();
+#endif /* SYM_LINUX_BOOT_COMMAND_LINE_SUPPORT */
+
+       /*
+        *  Allocate the device table since we donnot want to 
+        *  overflow the kernel stack.
+        *  1 x 4K PAGE is enough for more than 40 devices for i386.
+        */
+       devtbl = sym_calloc(PAGE_SIZE, "DEVTBL");
+       if (!devtbl)
+               return 0;
+
+       /*
+        *  Detect all NCR PQS/PDS memory controllers.
+        */
+#ifdef SYM_CONF_PQS_PDS_SUPPORT
+       sym_detect_pqs_pds();
+#endif
+
+       /* 
+        *  Detect all 53c8xx hosts.
+        *  Save the first Symbios NVRAM content if any 
+        *  for the boot order.
+        */
+       chips   = sizeof(sym_chip_ids)  / sizeof(sym_chip_ids[0]);
+       hosts   = PAGE_SIZE             / sizeof(*devtbl);
+#if SYM_CONF_NVRAM_SUPPORT
+       nvp = (sym_driver_setup.use_nvram & 0x1) ? &nvram0 : 0;
+#endif
+       j = 0;
+       count = 0;
+       pcidev = PCIDEV_NULL;
+       while (1) {
+               char *msg = "";
+               if (count >= hosts)
+                       break;
+               if (j >= chips)
+                       break;
+               i = sym_driver_setup.reverse_probe ? chips - 1 - j : j;
+               pcidev = pci_find_device(PCI_VENDOR_ID_NCR, sym_chip_ids[i],
+                                        pcidev);
+               if (pcidev == PCIDEV_NULL) {
+                       ++j;
+                       continue;
+               }
+               /* This one is guaranteed by AC to do nothing :-) */
+               if (pci_enable_device(pcidev))
+                       continue;
+               /* Some HW as the HP LH4 may report twice PCI devices */
+               for (i = 0; i < count ; i++) {
+                       if (devtbl[i].s.bus       == PciBusNumber(pcidev) && 
+                           devtbl[i].s.device_fn == PciDeviceFn(pcidev))
+                               break;
+               }
+               if (i != count) /* Ignore this device if we already have it */
+                       continue;
+               devp = &devtbl[count];
+               devp->host_id = SYM_SETUP_HOST_ID;
+               devp->attach_done = 0;
+               if (sym53c8xx_pci_init(tpnt, pcidev, devp)) {
+                       continue;
+               }
+               ++count;
+#if SYM_CONF_NVRAM_SUPPORT
+               if (nvp) {
+                       sym_get_nvram(devp, nvp);
+                       switch(nvp->type) {
+                       case SYM_SYMBIOS_NVRAM:
+                               /*
+                                *   Switch to the other nvram buffer, so that 
+                                *   nvram0 will contain the first Symbios 
+                                *   format NVRAM content with boot order.
+                                */
+                               nvp = &nvram;
+                               msg = "with Symbios NVRAM";
+                               break;
+                       case SYM_TEKRAM_NVRAM:
+                               msg = "with Tekram NVRAM";
+                               break;
+                       }
+               }
+#endif
+#ifdef SYM_CONF_PQS_PDS_SUPPORT
+               /*
+                *  Match the BUS number for PQS/PDS devices.
+                *  Read the SCSI ID from a special register mapped
+                *  into the configuration space of the individual
+                *  875s.  This register is set up by the PQS bios
+                */
+               for(i = 0; i < SYM_CONF_MAX_PQS_BUS && pqs_bus[i] != -1; i++) {
+                       u_char tmp;
+                       if (pqs_bus[i] == PciBusNumber(pcidev)) {
+                               pci_read_config_byte(pcidev, 0x84, &tmp);
+                               devp->pqs_pds = 1;
+                               devp->host_id = tmp;
+                               break;
+                       }
+               }
+               if (devp->pqs_pds)
+                       msg = "(NCR PQS/PDS)";
+#endif
+               if (boot_verbose)
+                       printf_info("%s: 53c%s detected %s\n",
+                                   sym_name(devp), devp->chip.name, msg);
+       }
+
+       /*
+        *  If we have found a SYMBIOS NVRAM, use first the NVRAM boot 
+        *  sequence as device boot order.
+        *  check devices in the boot record against devices detected. 
+        *  attach devices if we find a match. boot table records that 
+        *  do not match any detected devices will be ignored. 
+        *  devices that do not match any boot table will not be attached
+        *  here but will attempt to be attached during the device table 
+        *  rescan.
+        */
+#if SYM_CONF_NVRAM_SUPPORT
+       if (!nvp || nvram0.type != SYM_SYMBIOS_NVRAM)
+               goto next;
+       for (i = 0; i < 4; i++) {
+               Symbios_host *h = &nvram0.data.Symbios.host[i];
+               for (j = 0 ; j < count ; j++) {
+                       devp = &devtbl[j];
+                       if (h->device_fn != devp->s.device_fn ||
+                           h->bus_nr    != devp->s.bus  ||
+                           h->device_id != devp->chip.device_id)
+                               continue;
+                       if (devp->attach_done)
+                               continue;
+                       if (h->flags & SYMBIOS_INIT_SCAN_AT_BOOT) {
+                               sym_get_nvram(devp, nvp);
+                               if (!sym_attach (tpnt, attach_count, devp))
+                                       attach_count++;
+                       }
+                       else if (!(sym_driver_setup.use_nvram & 0x80))
+                               printf_info(
+                                     "%s: 53c%s state OFF thus not attached\n",
+                                     sym_name(devp), devp->chip.name);
+                       else
+                               continue;
+
+                       devp->attach_done = 1;
+                       break;
+               }
+       }
+next:
+#endif
+
+       /* 
+        *  Rescan device list to make sure all boards attached.
+        *  Devices without boot records will not be attached yet
+        *  so try to attach them here.
+        */
+       for (i= 0; i < count; i++) {
+               devp = &devtbl[i];
+               if (!devp->attach_done) {
+                       devp->nvram = &nvram;
+                       nvram.type = 0;
+#if SYM_CONF_NVRAM_SUPPORT
+                       sym_get_nvram(devp, nvp);
+#endif
+                       if (!sym_attach (tpnt, attach_count, devp))
+                               attach_count++;
+               }
+       }
+
+       sym_mfree(devtbl, PAGE_SIZE, "DEVTBL");
+
+       return attach_count;
+}
+
+
+
+#ifdef MODULE
+/*
+ *  Linux release module stuff.
+ *
+ *  Called before unloading the module.
+ *  Detach the host.
+ *  We have to free resources and halt the NCR chip.
+ *
+ */
+static int sym_detach(hcb_p np)
+{
+       printk("%s: detaching ...\n", sym_name(np));
+
+       /*
+        *  Try to delete the timer.
+        *  In the unlikely situation where this failed,
+        *  try to synchronize with the timer handler.
+        */
+#if LINUX_VERSION_CODE < LinuxVersionCode(2, 4, 0)
+       np->s.release_stage = 1;
+       if (!del_timer(&np->s.timer)) {
+               int i = 1000;
+               int k = 1;
+               while (1) {
+                       u_long flags;
+                       SYM_LOCK_HCB(np, flags);
+                       k = np->s.release_stage;
+                       SYM_UNLOCK_HCB(np, flags);
+                       if (k == 2 || !--i)
+                               break;
+                       MDELAY(5);
+               }
+               if (!i)
+                       printk("%s: failed to kill timer!\n", sym_name(np));
+       }
+       np->s.release_stage = 2;
+#else
+       (void)del_timer_sync(&np->s.timer);
+#endif
+
+       /*
+        *  Reset NCR chip.
+        *  We should use sym_soft_reset(), but we donnot want to do 
+        *  so, since we may not be safe if interrupts occur.
+        */
+       printk("%s: resetting chip\n", sym_name(np));
+       OUTB (nc_istat, SRST);
+       UDELAY (10);
+       OUTB (nc_istat, 0);
+
+       /*
+        *  Free host resources
+        */
+       sym_free_resources(np);
+
+       return 1;
+}
+
+int sym53c8xx_release(struct Scsi_Host *host)
+{
+     sym_detach(((struct host_data *) host->hostdata)->ncb);
+
+     return 0;
+}
+#endif /* MODULE */
+
+/*
+ * For bigots to keep silent. :)
+ */
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+
+/*
+ * Driver host template.
+ */
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
+static
+#endif
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0) || defined(MODULE)
+Scsi_Host_Template driver_template = SYM53C8XX;
+#include "../scsi_module.c.inc"
+#endif
diff --git a/xen/drivers/scsi/sym53c8xx_2/sym_glue.h b/xen/drivers/scsi/sym53c8xx_2/sym_glue.h
new file mode 100644 (file)
index 0000000..78c44ba
--- /dev/null
@@ -0,0 +1,683 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000  Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ *         Wolfgang Stanglmeier        <wolf@cologne.de>
+ *         Stefan Esser                <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994  Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef SYM_GLUE_H
+#define SYM_GLUE_H
+
+#if 0
+#define SYM_CONF_DMA_ADDRESSING_MODE 2
+#endif
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#include <xeno/version.h>
+#if    LINUX_VERSION_CODE < LinuxVersionCode(2, 2, 0)
+#error "This driver requires a kernel version not lower than 2.2.0"
+#endif
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17)
+#include <xeno/spinlock.h>
+#else
+#include <asm/spinlock.h>
+#endif
+#include <xeno/delay.h>
+
+/* SAE: Not here... */
+#ifdef XENO_KILLED
+#include <xeno/signal.h>
+#endif
+
+#include <xeno/sched.h>
+#include <xeno/errno.h>
+#include <xeno/pci.h>
+#include <xeno/string.h>
+#include <xeno/mm.h>
+#include <xeno/ioport.h>
+#include <xeno/time.h>
+#include <xeno/timer.h>
+/* SAE */
+#ifdef XENO_KILLED
+#include <xeno/stat.h>
+#endif
+
+#include <xeno/blk.h>
+
+#ifdef __sparc__
+#  include <asm/irq.h>
+#endif
+#include <xeno/init.h>
+
+#ifndef        __init
+#define        __init
+#endif
+#ifndef        __initdata
+#define        __initdata
+#endif
+
+#include "../scsi.h"
+#include "../hosts.h"
+#include "../constants.h"
+#include "../sd.h"
+
+#include <xeno/types.h>
+
+/*
+ *  Define BITS_PER_LONG for earlier linux versions.
+ */
+#ifndef        BITS_PER_LONG
+#if (~0UL) == 0xffffffffUL
+#define        BITS_PER_LONG   32
+#else
+#define        BITS_PER_LONG   64
+#endif
+#endif
+
+typedef        u_long  vm_offset_t;
+
+#ifndef bcopy
+#define bcopy(s, d, n) memcpy((d), (s), (n))
+#endif
+
+#ifndef bzero
+#define bzero(d, n)    memset((d), 0, (n))
+#endif
+
+#ifndef bcmp
+#define bcmp(a, b, n)  memcmp((a), (b), (n))
+#endif
+
+/*
+ *  General driver includes.
+ */
+#include "sym53c8xx.h"
+#include "sym_misc.h"
+#include "sym_conf.h"
+#include "sym_defs.h"
+
+/*
+ * Configuration addendum for Linux.
+ */
+#if    LINUX_VERSION_CODE >= LinuxVersionCode(2,3,47)
+#define        SYM_LINUX_DYNAMIC_DMA_MAPPING
+#endif
+
+#define        SYM_CONF_TIMER_INTERVAL         ((HZ+1)/2)
+
+#define SYM_OPT_HANDLE_DIR_UNKNOWN
+#define SYM_OPT_HANDLE_DEVICE_QUEUEING
+#define SYM_OPT_NVRAM_PRE_READ
+#define SYM_OPT_SNIFF_INQUIRY
+#define SYM_OPT_LIMIT_COMMAND_REORDERING
+#define        SYM_OPT_ANNOUNCE_TRANSFER_RATE
+
+#ifdef SYM_LINUX_DYNAMIC_DMA_MAPPING
+#define        SYM_OPT_BUS_DMA_ABSTRACTION
+#endif
+
+/*
+ *  Print a message with severity.
+ */
+#define printf_emerg(args...)  printk(KERN_EMERG args)
+#define        printf_alert(args...)   printk(KERN_ALERT args)
+#define        printf_crit(args...)    printk(KERN_CRIT args)
+#define        printf_err(args...)     printk(KERN_ERR args)
+#define        printf_warning(args...) printk(KERN_WARNING args)
+#define        printf_notice(args...)  printk(KERN_NOTICE args)
+#define        printf_info(args...)    printk(KERN_INFO args)
+#define        printf_debug(args...)   printk(KERN_DEBUG args)
+#define        printf(args...)         printk(args)
+
+/*
+ *  Insert a delay in micro-seconds and milli-seconds.
+ */
+void sym_udelay(int us);
+void sym_mdelay(int ms);
+
+/*
+ *  Let the compiler know about driver data structure names.
+ */
+typedef struct sym_tcb *tcb_p;
+typedef struct sym_lcb *lcb_p;
+typedef struct sym_ccb *ccb_p;
+typedef struct sym_hcb *hcb_p;
+typedef struct sym_stcb *stcb_p;
+typedef struct sym_slcb *slcb_p;
+typedef struct sym_sccb *sccb_p;
+typedef struct sym_shcb *shcb_p;
+
+/*
+ *  Define a reference to the O/S dependant IO request.
+ */
+typedef Scsi_Cmnd *cam_ccb_p;  /* Generic */
+typedef Scsi_Cmnd *cam_scsiio_p;/* SCSI I/O */
+
+
+/*
+ *  IO functions definition for big/little endian CPU support.
+ *  For now, PCI chips are only supported in little endian addressing mode, 
+ */
+
+#ifdef __BIG_ENDIAN
+
+#define        inw_l2b         inw
+#define        inl_l2b         inl
+#define        outw_b2l        outw
+#define        outl_b2l        outl
+#define        readw_l2b       readw
+#define        readl_l2b       readl
+#define        writew_b2l      writew
+#define        writel_b2l      writel
+
+#else  /* little endian */
+
+#if defined(__i386__)  /* i386 implements full FLAT memory/MMIO model */
+#define        inw_raw         inw
+#define        inl_raw         inl
+#define        outw_raw        outw
+#define        outl_raw        outl
+#define readb_raw(a)   (*(volatile unsigned char *) (a))
+#define readw_raw(a)   (*(volatile unsigned short *) (a))
+#define readl_raw(a)   (*(volatile unsigned int *) (a))
+#define writeb_raw(b,a)        ((*(volatile unsigned char *) (a)) = (b))
+#define writew_raw(b,a)        ((*(volatile unsigned short *) (a)) = (b))
+#define writel_raw(b,a)        ((*(volatile unsigned int *) (a)) = (b))
+
+#else  /* Other little-endian */
+#define        inw_raw         inw
+#define        inl_raw         inl
+#define        outw_raw        outw
+#define        outl_raw        outl
+#define        readw_raw       readw
+#define        readl_raw       readl
+#define        writew_raw      writew
+#define        writel_raw      writel
+
+#endif
+#endif
+
+#ifdef SYM_CONF_CHIP_BIG_ENDIAN
+#error "Chips in BIG ENDIAN addressing mode are not (yet) supported"
+#endif
+
+
+/*
+ *  If the chip uses big endian addressing mode over the 
+ *  PCI, actual io register addresses for byte and word 
+ *  accesses must be changed according to lane routing.
+ *  Btw, sym_offb() and sym_offw() macros only apply to 
+ *  constants and so donnot generate bloated code.
+ */
+
+#if    defined(SYM_CONF_CHIP_BIG_ENDIAN)
+
+#define sym_offb(o)    (((o)&~3)+((~((o)&3))&3))
+#define sym_offw(o)    (((o)&~3)+((~((o)&3))&2))
+
+#else
+
+#define sym_offb(o)    (o)
+#define sym_offw(o)    (o)
+
+#endif
+
+/*
+ *  If the CPU and the chip use same endian-ness adressing,
+ *  no byte reordering is needed for script patching.
+ *  Macro cpu_to_scr() is to be used for script patching.
+ *  Macro scr_to_cpu() is to be used for getting a DWORD 
+ *  from the script.
+ */
+
+#if    defined(__BIG_ENDIAN) && !defined(SYM_CONF_CHIP_BIG_ENDIAN)
+
+#define cpu_to_scr(dw) cpu_to_le32(dw)
+#define scr_to_cpu(dw) le32_to_cpu(dw)
+
+#elif  defined(__LITTLE_ENDIAN) && defined(SYM_CONF_CHIP_BIG_ENDIAN)
+
+#define cpu_to_scr(dw) cpu_to_be32(dw)
+#define scr_to_cpu(dw) be32_to_cpu(dw)
+
+#else
+
+#define cpu_to_scr(dw) (dw)
+#define scr_to_cpu(dw) (dw)
+
+#endif
+
+/*
+ *  Access to the controller chip.
+ *
+ *  If SYM_CONF_IOMAPPED is defined, the driver will use 
+ *  normal IOs instead of the MEMORY MAPPED IO method  
+ *  recommended by PCI specifications.
+ *  If all PCI bridges, host brigdes and architectures 
+ *  would have been correctly designed for PCI, this 
+ *  option would be useless.
+ *
+ *  If the CPU and the chip use same endian-ness adressing,
+ *  no byte reordering is needed for accessing chip io 
+ *  registers. Functions suffixed by '_raw' are assumed 
+ *  to access the chip over the PCI without doing byte 
+ *  reordering. Functions suffixed by '_l2b' are 
+ *  assumed to perform little-endian to big-endian byte 
+ *  reordering, those suffixed by '_b2l' blah, blah,
+ *  blah, ...
+ */
+
+#if defined(SYM_CONF_IOMAPPED)
+
+/*
+ *  IO mapped only input / ouput
+ */
+
+#define        INB_OFF(o)        inb (np->s.io_port + sym_offb(o))
+#define        OUTB_OFF(o, val)  outb ((val), np->s.io_port + sym_offb(o))
+
+#if    defined(__BIG_ENDIAN) && !defined(SYM_CONF_CHIP_BIG_ENDIAN)
+
+#define        INW_OFF(o)        inw_l2b (np->s.io_port + sym_offw(o))
+#define        INL_OFF(o)        inl_l2b (np->s.io_port + (o))
+
+#define        OUTW_OFF(o, val)  outw_b2l ((val), np->s.io_port + sym_offw(o))
+#define        OUTL_OFF(o, val)  outl_b2l ((val), np->s.io_port + (o))
+
+#elif  defined(__LITTLE_ENDIAN) && defined(SYM_CONF_CHIP_BIG_ENDIAN)
+
+#define        INW_OFF(o)        inw_b2l (np->s.io_port + sym_offw(o))
+#define        INL_OFF(o)        inl_b2l (np->s.io_port + (o))
+
+#define        OUTW_OFF(o, val)  outw_l2b ((val), np->s.io_port + sym_offw(o))
+#define        OUTL_OFF(o, val)  outl_l2b ((val), np->s.io_port + (o))
+
+#else
+
+#define        INW_OFF(o)        inw_raw (np->s.io_port + sym_offw(o))
+#define        INL_OFF(o)        inl_raw (np->s.io_port + (o))
+
+#define        OUTW_OFF(o, val)  outw_raw ((val), np->s.io_port + sym_offw(o))
+#define        OUTL_OFF(o, val)  outl_raw ((val), np->s.io_port + (o))
+
+#endif /* ENDIANs */
+
+#else  /* defined SYM_CONF_IOMAPPED */
+
+/*
+ *  MEMORY mapped IO input / output
+ */
+
+#define INB_OFF(o)        readb((char *)np->s.mmio_va + sym_offb(o))
+#define OUTB_OFF(o, val)  writeb((val), (char *)np->s.mmio_va + sym_offb(o))
+
+#if    defined(__BIG_ENDIAN) && !defined(SYM_CONF_CHIP_BIG_ENDIAN)
+
+#define INW_OFF(o)        readw_l2b((char *)np->s.mmio_va + sym_offw(o))
+#define INL_OFF(o)        readl_l2b((char *)np->s.mmio_va + (o))
+
+#define OUTW_OFF(o, val)  writew_b2l((val), (char *)np->s.mmio_va + sym_offw(o))
+#define OUTL_OFF(o, val)  writel_b2l((val), (char *)np->s.mmio_va + (o))
+
+#elif  defined(__LITTLE_ENDIAN) && defined(SYM_CONF_CHIP_BIG_ENDIAN)
+
+#define INW_OFF(o)        readw_b2l((char *)np->s.mmio_va + sym_offw(o))
+#define INL_OFF(o)        readl_b2l((char *)np->s.mmio_va + (o))
+
+#define OUTW_OFF(o, val)  writew_l2b((val), (char *)np->s.mmio_va + sym_offw(o))
+#define OUTL_OFF(o, val)  writel_l2b((val), (char *)np->s.mmio_va + (o))
+
+#else
+
+#define INW_OFF(o)        readw_raw((char *)np->s.mmio_va + sym_offw(o))
+#define INL_OFF(o)        readl_raw((char *)np->s.mmio_va + (o))
+
+#define OUTW_OFF(o, val)  writew_raw((val), (char *)np->s.mmio_va + sym_offw(o))
+#define OUTL_OFF(o, val)  writel_raw((val), (char *)np->s.mmio_va + (o))
+
+#endif
+
+#endif /* defined SYM_CONF_IOMAPPED */
+
+#define OUTRAM_OFF(o, a, l) memcpy_toio(np->s.ram_va + (o), (a), (l))
+
+/*
+ *  Remap some status field values.
+ */
+#define CAM_REQ_CMP            DID_OK
+#define CAM_SEL_TIMEOUT                DID_NO_CONNECT
+#define CAM_CMD_TIMEOUT                DID_TIME_OUT
+#define CAM_REQ_ABORTED                DID_ABORT
+#define CAM_UNCOR_PARITY       DID_PARITY
+#define CAM_SCSI_BUS_RESET     DID_RESET       
+#define CAM_REQUEUE_REQ                DID_SOFT_ERROR
+#define        CAM_UNEXP_BUSFREE       DID_ERROR
+#define        CAM_SCSI_BUSY           DID_BUS_BUSY
+
+#define        CAM_DEV_NOT_THERE       DID_NO_CONNECT
+#define        CAM_REQ_INVALID         DID_ERROR
+#define        CAM_REQ_TOO_BIG         DID_ERROR
+
+#define        CAM_RESRC_UNAVAIL       DID_ERROR
+
+/*
+ *  Remap SCSI data direction values.
+ */
+#ifndef        SCSI_DATA_UNKNOWN
+#define        SCSI_DATA_UNKNOWN       0
+#define        SCSI_DATA_WRITE         1
+#define        SCSI_DATA_READ          2
+#define        SCSI_DATA_NONE          3
+#endif
+#define CAM_DIR_NONE           SCSI_DATA_NONE
+#define CAM_DIR_IN             SCSI_DATA_READ
+#define CAM_DIR_OUT            SCSI_DATA_WRITE
+#define CAM_DIR_UNKNOWN                SCSI_DATA_UNKNOWN
+
+/*
+ *  These ones are used as return code from 
+ *  error recovery handlers under Linux.
+ */
+#define SCSI_SUCCESS   SUCCESS
+#define SCSI_FAILED    FAILED
+
+/*
+ *  System specific target data structure.
+ *  None for now, under Linux.
+ */
+/* #define SYM_HAVE_STCB */
+
+/*
+ *  System specific lun data structure.
+ */
+#define SYM_HAVE_SLCB
+struct sym_slcb {
+       u_short reqtags;        /* Number of tags requested by user */
+       u_short scdev_depth;    /* Queue depth set in select_queue_depth() */
+};
+
+/*
+ *  System specific command data structure.
+ *  Not needed under Linux.
+ */
+/* struct sym_sccb */
+
+/*
+ *  System specific host data structure.
+ */
+struct sym_shcb {
+       /*
+        *  Chip and controller indentification.
+        */
+       int             unit;
+       char            inst_name[16];
+       char            chip_name[8];
+       struct pci_dev  *device;
+
+       u_char          bus;            /* PCI BUS number               */
+       u_char          device_fn;      /* PCI BUS device and function  */
+
+       spinlock_t      smp_lock;       /* Lock for SMP threading       */
+
+       vm_offset_t     mmio_va;        /* MMIO kernel virtual address  */
+       vm_offset_t     ram_va;         /* RAM  kernel virtual address  */
+       u_long          io_port;        /* IO port address cookie       */
+       u_short         io_ws;          /* IO window size               */
+       int             irq;            /* IRQ number                   */
+
+       SYM_QUEHEAD     wait_cmdq;      /* Awaiting SCSI commands       */
+       SYM_QUEHEAD     busy_cmdq;      /* Enqueued SCSI commands       */
+
+       struct timer_list timer;        /* Timer handler link header    */
+       u_long          lasttime;
+       u_long          settle_time;    /* Resetting the SCSI BUS       */
+       u_char          settle_time_valid;
+#if LINUX_VERSION_CODE < LinuxVersionCode(2, 4, 0)
+       u_char          release_stage;  /* Synchronisation on release   */
+#endif
+};
+
+/*
+ *  Return the name of the controller.
+ */
+#define sym_name(np) (np)->s.inst_name
+
+/*
+ *  Data structure used as input for the NVRAM reading.
+ *  Must resolve the IO macros and sym_name(), when  
+ *  used as sub-field 's' of another structure.
+ */
+typedef struct {
+       int     bus;
+       u_char  device_fn;
+       u_long  base;
+       u_long  base_2;
+       u_long  base_c;
+       u_long  base_2_c;
+       int     irq;
+/* port and address fields to fit INB, OUTB macros */
+       u_long  io_port;
+       vm_offset_t mmio_va;
+       char    inst_name[16];
+} sym_slot;
+
+typedef struct sym_nvram sym_nvram;
+typedef struct sym_pci_chip sym_chip;
+
+typedef struct {
+       struct pci_dev *pdev;
+       sym_slot  s;
+       sym_chip  chip;
+       sym_nvram *nvram;
+       u_short device_id;
+       u_char host_id;
+#ifdef SYM_CONF_PQS_PDS_SUPPORT
+       u_char pqs_pds;
+#endif
+       int attach_done;
+} sym_device;
+
+typedef sym_device *sdev_p;
+
+/*
+ *  The driver definitions (sym_hipd.h) must know about a 
+ *  couple of things related to the memory allocator.
+ */
+typedef u_long m_addr_t;       /* Enough bits to represent any address */
+#define SYM_MEM_PAGE_ORDER 0   /* 1 PAGE  maximum */
+#define SYM_MEM_CLUSTER_SHIFT  (PAGE_SHIFT+SYM_MEM_PAGE_ORDER)
+#ifdef MODULE
+#define SYM_MEM_FREE_UNUSED    /* Free unused pages immediately */
+#endif
+#ifdef SYM_LINUX_DYNAMIC_DMA_MAPPING
+typedef struct pci_dev *m_pool_ident_t;
+#endif
+
+/*
+ *  Include driver soft definitions.
+ */
+#include "sym_fw.h"
+#include "sym_hipd.h"
+
+/*
+ *  Memory allocator related stuff.
+ */
+
+#define SYM_MEM_GFP_FLAGS      GFP_ATOMIC
+#define SYM_MEM_WARN   1       /* Warn on failed operations */
+
+#define sym_get_mem_cluster()  \
+       __get_free_pages(SYM_MEM_GFP_FLAGS, SYM_MEM_PAGE_ORDER)
+#define sym_free_mem_cluster(p)        \
+       free_pages(p, SYM_MEM_PAGE_ORDER)
+
+void *sym_calloc(int size, char *name);
+void sym_mfree(void *m, int size, char *name);
+
+#ifndef        SYM_LINUX_DYNAMIC_DMA_MAPPING
+/*
+ *  Simple case.
+ *  All the memory assummed DMAable and O/S providing virtual 
+ *  to bus physical address translation.
+ */
+#define __sym_calloc_dma(pool_id, size, name)  sym_calloc(size, name)
+#define __sym_mfree_dma(pool_id, m, size, name)        sym_mfree(m, size, name)
+#define __vtobus(b, p)                         virt_to_bus(p)
+
+#else  /* SYM_LINUX_DYNAMIC_DMA_MAPPING */
+/*
+ *  Complex case.
+ *  We have to provide the driver memory allocator with methods for 
+ *  it to maintain virtual to bus physical address translations.
+ */
+
+#define sym_m_pool_match(mp_id1, mp_id2)       (mp_id1 == mp_id2)
+
+static __inline m_addr_t sym_m_get_dma_mem_cluster(m_pool_p mp, m_vtob_p vbp)
+{
+       void *vaddr = 0;
+       dma_addr_t baddr = 0;
+
+       vaddr = pci_alloc_consistent(mp->dev_dmat,SYM_MEM_CLUSTER_SIZE, &baddr);
+       if (vaddr) {
+               vbp->vaddr = (m_addr_t) vaddr;
+               vbp->baddr = (m_addr_t) baddr;
+       }
+       return (m_addr_t) vaddr;
+}
+
+static __inline void sym_m_free_dma_mem_cluster(m_pool_p mp, m_vtob_p vbp)
+{
+       pci_free_consistent(mp->dev_dmat, SYM_MEM_CLUSTER_SIZE,
+                           (void *)vbp->vaddr, (dma_addr_t)vbp->baddr);
+}
+
+#define sym_m_create_dma_mem_tag(mp)   (0)
+
+#define sym_m_delete_dma_mem_tag(mp)   do { ; } while (0)
+
+void *__sym_calloc_dma(m_pool_ident_t dev_dmat, int size, char *name);
+void __sym_mfree_dma(m_pool_ident_t dev_dmat, void *m, int size, char *name);
+m_addr_t __vtobus(m_pool_ident_t dev_dmat, void *m);
+
+#endif /* SYM_LINUX_DYNAMIC_DMA_MAPPING */
+
+/*
+ *  Set the status field of a CAM CCB.
+ */
+static __inline void 
+sym_set_cam_status(Scsi_Cmnd  *ccb, int status)
+{
+       ccb->result &= ~(0xff  << 16);
+       ccb->result |= (status << 16);
+}
+
+/*
+ *  Get the status field of a CAM CCB.
+ */
+static __inline int 
+sym_get_cam_status(Scsi_Cmnd  *ccb)
+{
+       return ((ccb->result >> 16) & 0xff);
+}
+
+/*
+ *  The dma mapping is mostly handled by the 
+ *  SCSI layer and the driver glue under Linux.
+ */
+#define sym_data_dmamap_create(np, cp)         (0)
+#define sym_data_dmamap_destroy(np, cp)                do { ; } while (0)
+#define sym_data_dmamap_unload(np, cp)         do { ; } while (0)
+#define sym_data_dmamap_presync(np, cp)                do { ; } while (0)
+#define sym_data_dmamap_postsync(np, cp)       do { ; } while (0)
+
+/*
+ *  Async handler for negotiations.
+ */
+void sym_xpt_async_nego_wide(hcb_p np, int target);
+#define sym_xpt_async_nego_sync(np, target)    \
+       sym_announce_transfer_rate(np, target)
+#define sym_xpt_async_nego_ppr(np, target)     \
+       sym_announce_transfer_rate(np, target)
+
+/*
+ *  Build CAM result for a successful IO and for a failed IO.
+ */
+static __inline void sym_set_cam_result_ok(hcb_p np, ccb_p cp, int resid)
+{
+       Scsi_Cmnd *cmd = cp->cam_ccb;
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,99)
+       cmd->resid = resid;
+#endif
+       cmd->result = (((DID_OK) << 16) + ((cp->ssss_status) & 0x7f));
+}
+void sym_set_cam_result_error(hcb_p np, ccb_p cp, int resid);
+
+/*
+ *  Other O/S specific methods.
+ */
+#define sym_cam_target_id(ccb) (ccb)->target
+#define sym_cam_target_lun(ccb)        (ccb)->lun
+#define        sym_freeze_cam_ccb(ccb) do { ; } while (0)
+void sym_xpt_done(hcb_p np, cam_ccb_p ccb);
+void sym_xpt_done2(hcb_p np, cam_ccb_p ccb, int cam_status);
+void sym_print_addr (ccb_p cp);
+void sym_xpt_async_bus_reset(hcb_p np);
+void sym_xpt_async_sent_bdr(hcb_p np, int target);
+int  sym_setup_data_and_start (hcb_p np, cam_scsiio_p csio, ccb_p cp);
+void sym_log_bus_error(hcb_p np);
+#ifdef SYM_OPT_SNIFF_INQUIRY
+void sym_sniff_inquiry(hcb_p np, Scsi_Cmnd *cmd, int resid);
+#endif
+
+#endif /* SYM_GLUE_H */
diff --git a/xen/drivers/scsi/sym53c8xx_2/sym_hipd.c b/xen/drivers/scsi/sym53c8xx_2/sym_hipd.c
new file mode 100644 (file)
index 0000000..f8e77c9
--- /dev/null
@@ -0,0 +1,6021 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000  Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ *         Wolfgang Stanglmeier        <wolf@cologne.de>
+ *         Stefan Esser                <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994  Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define SYM_DRIVER_NAME        "sym-2.1.17a"
+
+#ifdef __FreeBSD__
+#include <dev/sym/sym_glue.h>
+#else
+#include "sym_glue.h"
+#endif
+
+#if 0
+#define SYM_DEBUG_GENERIC_SUPPORT
+#endif
+
+/*
+ *  Needed function prototypes.
+ */
+static void sym_int_ma (hcb_p np);
+static void sym_int_sir (hcb_p np);
+static ccb_p sym_alloc_ccb(hcb_p np);
+static ccb_p sym_ccb_from_dsa(hcb_p np, u32 dsa);
+static void sym_alloc_lcb_tags (hcb_p np, u_char tn, u_char ln);
+static void sym_complete_error (hcb_p np, ccb_p cp);
+static void sym_complete_ok (hcb_p np, ccb_p cp);
+static int sym_compute_residual(hcb_p np, ccb_p cp);
+
+/*
+ *  Returns the name of this driver.
+ */
+char *sym_driver_name(void)
+{
+       return SYM_DRIVER_NAME;
+}
+/*
+ *  Print a buffer in hexadecimal format.
+ */
+static void sym_printb_hex (u_char *p, int n)
+{
+       while (n-- > 0)
+               printf (" %x", *p++);
+}
+
+/*
+ *  Same with a label at beginning and .\n at end.
+ */
+static void sym_printl_hex (char *label, u_char *p, int n)
+{
+       printf ("%s", label);
+       sym_printb_hex (p, n);
+       printf (".\n");
+}
+
+/*
+ *  Print something which allows to retrieve the controler type, 
+ *  unit, target, lun concerned by a kernel message.
+ */
+static void sym_print_target (hcb_p np, int target)
+{
+       printf ("%s:%d:", sym_name(np), target);
+}
+
+static void sym_print_lun(hcb_p np, int target, int lun)
+{
+       printf ("%s:%d:%d:", sym_name(np), target, lun);
+}
+
+/*
+ *  Print out the content of a SCSI message.
+ */
+static int sym_show_msg (u_char * msg)
+{
+       u_char i;
+       printf ("%x",*msg);
+       if (*msg==M_EXTENDED) {
+               for (i=1;i<8;i++) {
+                       if (i-1>msg[1]) break;
+                       printf ("-%x",msg[i]);
+               };
+               return (i+1);
+       } else if ((*msg & 0xf0) == 0x20) {
+               printf ("-%x",msg[1]);
+               return (2);
+       };
+       return (1);
+}
+
+static void sym_print_msg (ccb_p cp, char *label, u_char *msg)
+{
+       PRINT_ADDR(cp);
+       if (label)
+               printf ("%s: ", label);
+
+       (void) sym_show_msg (msg);
+       printf (".\n");
+}
+
+static void sym_print_nego_msg (hcb_p np, int target, char *label, u_char *msg)
+{
+       PRINT_TARGET(np, target);
+       if (label)
+               printf ("%s: ", label);
+
+       (void) sym_show_msg (msg);
+       printf (".\n");
+}
+
+/*
+ *  Print something that tells about extended errors.
+ */
+void sym_print_xerr(ccb_p cp, int x_status)
+{
+       if (x_status & XE_PARITY_ERR) {
+               PRINT_ADDR(cp);
+               printf ("unrecovered SCSI parity error.\n");
+       }
+       if (x_status & XE_EXTRA_DATA) {
+               PRINT_ADDR(cp);
+               printf ("extraneous data discarded.\n");
+       }
+       if (x_status & XE_BAD_PHASE) {
+               PRINT_ADDR(cp);
+               printf ("illegal scsi phase (4/5).\n");
+       }
+       if (x_status & XE_SODL_UNRUN) {
+               PRINT_ADDR(cp);
+               printf ("ODD transfer in DATA OUT phase.\n");
+       }
+       if (x_status & XE_SWIDE_OVRUN) {
+               PRINT_ADDR(cp);
+               printf ("ODD transfer in DATA IN phase.\n");
+       }
+}
+
+/*
+ *  Return a string for SCSI BUS mode.
+ */
+static char *sym_scsi_bus_mode(int mode)
+{
+       switch(mode) {
+       case SMODE_HVD: return "HVD";
+       case SMODE_SE:  return "SE";
+       case SMODE_LVD: return "LVD";
+       }
+       return "??";
+}
+
+/*
+ *  Soft reset the chip.
+ *
+ *  Raising SRST when the chip is running may cause 
+ *  problems on dual function chips (see below).
+ *  On the other hand, LVD devices need some delay 
+ *  to settle and report actual BUS mode in STEST4.
+ */
+static void sym_chip_reset (hcb_p np)
+{
+       OUTB (nc_istat, SRST);
+       UDELAY (10);
+       OUTB (nc_istat, 0);
+       UDELAY(2000);   /* For BUS MODE to settle */
+}
+
+/*
+ *  Really soft reset the chip.:)
+ *
+ *  Some 896 and 876 chip revisions may hang-up if we set 
+ *  the SRST (soft reset) bit at the wrong time when SCRIPTS 
+ *  are running.
+ *  So, we need to abort the current operation prior to 
+ *  soft resetting the chip.
+ */
+static void sym_soft_reset (hcb_p np)
+{
+       u_char istat;
+       int i;
+
+       if (!(np->features & FE_ISTAT1) || !(INB (nc_istat1) & SCRUN))
+               goto do_chip_reset;
+
+       OUTB (nc_istat, CABRT);
+       for (i = 100000 ; i ; --i) {
+               istat = INB (nc_istat);
+               if (istat & SIP) {
+                       INW (nc_sist);
+               }
+               else if (istat & DIP) {
+                       if (INB (nc_dstat) & ABRT)
+                               break;
+               }
+               UDELAY(5);
+       }
+       OUTB (nc_istat, 0);
+       if (!i)
+               printf("%s: unable to abort current chip operation, "
+                      "ISTAT=0x%02x.\n", sym_name(np), istat);
+do_chip_reset:
+       sym_chip_reset (np);
+}
+
+/*
+ *  Start reset process.
+ *
+ *  The interrupt handler will reinitialize the chip.
+ */
+static void sym_start_reset(hcb_p np)
+{
+       (void) sym_reset_scsi_bus(np, 1);
+}
+int sym_reset_scsi_bus(hcb_p np, int enab_int)
+{
+       u32 term;
+       int retv = 0;
+
+       sym_soft_reset(np);     /* Soft reset the chip */
+       if (enab_int)
+               OUTW (nc_sien, RST);
+       /*
+        *  Enable Tolerant, reset IRQD if present and 
+        *  properly set IRQ mode, prior to resetting the bus.
+        */
+       OUTB (nc_stest3, TE);
+       OUTB (nc_dcntl, (np->rv_dcntl & IRQM));
+       OUTB (nc_scntl1, CRST);
+       UDELAY (200);
+
+       if (!SYM_SETUP_SCSI_BUS_CHECK)
+               goto out;
+       /*
+        *  Check for no terminators or SCSI bus shorts to ground.
+        *  Read SCSI data bus, data parity bits and control signals.
+        *  We are expecting RESET to be TRUE and other signals to be 
+        *  FALSE.
+        */
+       term =  INB(nc_sstat0);
+       term =  ((term & 2) << 7) + ((term & 1) << 17); /* rst sdp0 */
+       term |= ((INB(nc_sstat2) & 0x01) << 26) |       /* sdp1     */
+               ((INW(nc_sbdl) & 0xff)   << 9)  |       /* d7-0     */
+               ((INW(nc_sbdl) & 0xff00) << 10) |       /* d15-8    */
+               INB(nc_sbcl);   /* req ack bsy sel atn msg cd io    */
+
+       if (!(np->features & FE_WIDE))
+               term &= 0x3ffff;
+
+       if (term != (2<<7)) {
+               printf("%s: suspicious SCSI data while resetting the BUS.\n",
+                       sym_name(np));
+               printf("%s: %sdp0,d7-0,rst,req,ack,bsy,sel,atn,msg,c/d,i/o = "
+                       "0x%lx, expecting 0x%lx\n",
+                       sym_name(np),
+                       (np->features & FE_WIDE) ? "dp1,d15-8," : "",
+                       (u_long)term, (u_long)(2<<7));
+               if (SYM_SETUP_SCSI_BUS_CHECK == 1)
+                       retv = 1;
+       }
+out:
+       OUTB (nc_scntl1, 0);
+       /* MDELAY(100); */
+       return retv;
+}
+
+/*
+ *  Select SCSI clock frequency
+ */
+static void sym_selectclock(hcb_p np, u_char scntl3)
+{
+       /*
+        *  If multiplier not present or not selected, leave here.
+        */
+       if (np->multiplier <= 1) {
+               OUTB(nc_scntl3, scntl3);
+               return;
+       }
+
+       if (sym_verbose >= 2)
+               printf ("%s: enabling clock multiplier\n", sym_name(np));
+
+       OUTB(nc_stest1, DBLEN);    /* Enable clock multiplier             */
+       /*
+        *  Wait for the LCKFRQ bit to be set if supported by the chip.
+        *  Otherwise wait 50 micro-seconds (at least).
+        */
+       if (np->features & FE_LCKFRQ) {
+               int i = 20;
+               while (!(INB(nc_stest4) & LCKFRQ) && --i > 0)
+                       UDELAY (20);
+               if (!i)
+                       printf("%s: the chip cannot lock the frequency\n",
+                               sym_name(np));
+       } else
+               UDELAY ((50+10));
+       OUTB(nc_stest3, HSC);           /* Halt the scsi clock          */
+       OUTB(nc_scntl3, scntl3);
+       OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock multiplier      */
+       OUTB(nc_stest3, 0x00);          /* Restart scsi clock           */
+}
+
+
+/*
+ *  Determine the chip's clock frequency.
+ *
+ *  This is essential for the negotiation of the synchronous 
+ *  transfer rate.
+ *
+ *  Note: we have to return the correct value.
+ *  THERE IS NO SAFE DEFAULT VALUE.
+ *
+ *  Most NCR/SYMBIOS boards are delivered with a 40 Mhz clock.
+ *  53C860 and 53C875 rev. 1 support fast20 transfers but 
+ *  do not have a clock doubler and so are provided with a 
+ *  80 MHz clock. All other fast20 boards incorporate a doubler 
+ *  and so should be delivered with a 40 MHz clock.
+ *  The recent fast40 chips (895/896/895A/1010) use a 40 Mhz base 
+ *  clock and provide a clock quadrupler (160 Mhz).
+ */
+
+/*
+ *  calculate SCSI clock frequency (in KHz)
+ */
+static unsigned getfreq (hcb_p np, int gen)
+{
+       unsigned int ms = 0;
+       unsigned int f;
+
+       /*
+        * Measure GEN timer delay in order 
+        * to calculate SCSI clock frequency
+        *
+        * This code will never execute too
+        * many loop iterations (if DELAY is 
+        * reasonably correct). It could get
+        * too low a delay (too high a freq.)
+        * if the CPU is slow executing the 
+        * loop for some reason (an NMI, for
+        * example). For this reason we will
+        * if multiple measurements are to be 
+        * performed trust the higher delay 
+        * (lower frequency returned).
+        */
+       OUTW (nc_sien , 0);     /* mask all scsi interrupts */
+       (void) INW (nc_sist);   /* clear pending scsi interrupt */
+       OUTB (nc_dien , 0);     /* mask all dma interrupts */
+       (void) INW (nc_sist);   /* another one, just to be sure :) */
+       /*
+        * The C1010-33 core does not report GEN in SIST,
+        * if this interrupt is masked in SIEN.
+        * I don't know yet if the C1010-66 behaves the same way.
+        */
+       if (np->features & FE_C10) {
+               OUTW (nc_sien, GEN);
+               OUTB (nc_istat1, SIRQD);
+       }
+       OUTB (nc_scntl3, 4);    /* set pre-scaler to divide by 3 */
+       OUTB (nc_stime1, 0);    /* disable general purpose timer */
+       OUTB (nc_stime1, gen);  /* set to nominal delay of 1<<gen * 125us */
+       while (!(INW(nc_sist) & GEN) && ms++ < 100000)
+               UDELAY (1000/4);/* count in 1/4 of ms */
+       OUTB (nc_stime1, 0);    /* disable general purpose timer */
+       /*
+        * Undo C1010-33 specific settings.
+        */
+       if (np->features & FE_C10) {
+               OUTW (nc_sien, 0);
+               OUTB (nc_istat1, 0);
+       }
+       /*
+        * set prescaler to divide by whatever 0 means
+        * 0 ought to choose divide by 2, but appears
+        * to set divide by 3.5 mode in my 53c810 ...
+        */
+       OUTB (nc_scntl3, 0);
+
+       /*
+        * adjust for prescaler, and convert into KHz 
+        */
+       f = ms ? ((1 << gen) * (4340*4)) / ms : 0;
+
+       /*
+        * The C1010-33 result is biased by a factor 
+        * of 2/3 compared to earlier chips.
+        */
+       if (np->features & FE_C10)
+               f = (f * 2) / 3;
+
+       if (sym_verbose >= 2)
+               printf ("%s: Delay (GEN=%d): %u msec, %u KHz\n",
+                       sym_name(np), gen, ms/4, f);
+
+       return f;
+}
+
+static unsigned sym_getfreq (hcb_p np)
+{
+       u_int f1, f2;
+       int gen = 8;
+
+       (void) getfreq (np, gen);       /* throw away first result */
+       f1 = getfreq (np, gen);
+       f2 = getfreq (np, gen);
+       if (f1 > f2) f1 = f2;           /* trust lower result   */
+       return f1;
+}
+
+/*
+ *  Get/probe chip SCSI clock frequency
+ */
+static void sym_getclock (hcb_p np, int mult)
+{
+       unsigned char scntl3 = np->sv_scntl3;
+       unsigned char stest1 = np->sv_stest1;
+       unsigned f1;
+
+       np->multiplier = 1;
+       f1 = 40000;
+       /*
+        *  True with 875/895/896/895A with clock multiplier selected
+        */
+       if (mult > 1 && (stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) {
+               if (sym_verbose >= 2)
+                       printf ("%s: clock multiplier found\n", sym_name(np));
+               np->multiplier = mult;
+       }
+
+       /*
+        *  If multiplier not found or scntl3 not 7,5,3,
+        *  reset chip and get frequency from general purpose timer.
+        *  Otherwise trust scntl3 BIOS setting.
+        */
+       if (np->multiplier != mult || (scntl3 & 7) < 3 || !(scntl3 & 1)) {
+               OUTB (nc_stest1, 0);            /* make sure doubler is OFF */
+               f1 = sym_getfreq (np);
+
+               if (sym_verbose)
+                       printf ("%s: chip clock is %uKHz\n", sym_name(np), f1);
+
+               if      (f1 <   45000)          f1 =  40000;
+               else if (f1 <   55000)          f1 =  50000;
+               else                            f1 =  80000;
+
+               if (f1 < 80000 && mult > 1) {
+                       if (sym_verbose >= 2)
+                               printf ("%s: clock multiplier assumed\n",
+                                       sym_name(np));
+                       np->multiplier  = mult;
+               }
+       } else {
+               if      ((scntl3 & 7) == 3)     f1 =  40000;
+               else if ((scntl3 & 7) == 5)     f1 =  80000;
+               else                            f1 = 160000;
+
+               f1 /= np->multiplier;
+       }
+
+       /*
+        *  Compute controller synchronous parameters.
+        */
+       f1              *= np->multiplier;
+       np->clock_khz   = f1;
+}
+
+/*
+ *  Get/probe PCI clock frequency
+ */
+static int sym_getpciclock (hcb_p np)
+{
+       int f = 0;
+
+       /*
+        *  For now, we only need to know about the actual 
+        *  PCI BUS clock frequency for C1010-66 chips.
+        */
+#if 1
+       if (np->features & FE_66MHZ) {
+#else
+       if (1) {
+#endif
+               OUTB (nc_stest1, SCLK); /* Use the PCI clock as SCSI clock */
+               f = (int) sym_getfreq (np);
+               OUTB (nc_stest1, 0);
+       }
+       np->pciclk_khz = f;
+
+       return f;
+}
+
+/*
+ *  SYMBIOS chip clock divisor table.
+ *
+ *  Divisors are multiplied by 10,000,000 in order to make 
+ *  calculations more simple.
+ */
+#define _5M 5000000
+static u32 div_10M[] = {2*_5M, 3*_5M, 4*_5M, 6*_5M, 8*_5M, 12*_5M, 16*_5M};
+
+/*
+ *  Get clock factor and sync divisor for a given 
+ *  synchronous factor period.
+ */
+static int 
+sym_getsync(hcb_p np, u_char dt, u_char sfac, u_char *divp, u_char *fakp)
+{
+       u32     clk = np->clock_khz;    /* SCSI clock frequency in kHz  */
+       int     div = np->clock_divn;   /* Number of divisors supported */
+       u32     fak;                    /* Sync factor in sxfer         */
+       u32     per;                    /* Period in tenths of ns       */
+       u32     kpc;                    /* (per * clk)                  */
+       int     ret;
+
+       /*
+        *  Compute the synchronous period in tenths of nano-seconds
+        */
+       if (dt && sfac <= 9)    per = 125;
+       else if (sfac <= 10)    per = 250;
+       else if (sfac == 11)    per = 303;
+       else if (sfac == 12)    per = 500;
+       else                    per = 40 * sfac;
+       ret = per;
+
+       kpc = per * clk;
+       if (dt)
+               kpc <<= 1;
+
+       /*
+        *  For earliest C10 revision 0, we cannot use extra 
+        *  clocks for the setting of the SCSI clocking.
+        *  Note that this limits the lowest sync data transfer 
+        *  to 5 Mega-transfers per second and may result in
+        *  using higher clock divisors.
+        */
+#if 1
+       if ((np->features & (FE_C10|FE_U3EN)) == FE_C10) {
+               /*
+                *  Look for the lowest clock divisor that allows an 
+                *  output speed not faster than the period.
+                */
+               while (div > 0) {
+                       --div;
+                       if (kpc > (div_10M[div] << 2)) {
+                               ++div;
+                               break;
+                       }
+               }
+               fak = 0;                        /* No extra clocks */
+               if (div == np->clock_divn) {    /* Are we too fast ? */
+                       ret = -1;
+               }
+               *divp = div;
+               *fakp = fak;
+               return ret;
+       }
+#endif
+
+       /*
+        *  Look for the greatest clock divisor that allows an 
+        *  input speed faster than the period.
+        */
+       while (div-- > 0)
+               if (kpc >= (div_10M[div] << 2)) break;
+
+       /*
+        *  Calculate the lowest clock factor that allows an output 
+        *  speed not faster than the period, and the max output speed.
+        *  If fak >= 1 we will set both XCLKH_ST and XCLKH_DT.
+        *  If fak >= 2 we will also set XCLKS_ST and XCLKS_DT.
+        */
+       if (dt) {
+               fak = (kpc - 1) / (div_10M[div] << 1) + 1 - 2;
+               /* ret = ((2+fak)*div_10M[div])/np->clock_khz; */
+       }
+       else {
+               fak = (kpc - 1) / div_10M[div] + 1 - 4;
+               /* ret = ((4+fak)*div_10M[div])/np->clock_khz; */
+       }
+
+       /*
+        *  Check against our hardware limits, or bugs :).
+        */
+       if (fak < 0)    {fak = 0; ret = -1;}
+       if (fak > 2)    {fak = 2; ret = -1;}
+
+       /*
+        *  Compute and return sync parameters.
+        */
+       *divp = div;
+       *fakp = fak;
+
+       return ret;
+}
+
+/*
+ *  SYMBIOS chips allow burst lengths of 2, 4, 8, 16, 32, 64,
+ *  128 transfers. All chips support at least 16 transfers 
+ *  bursts. The 825A, 875 and 895 chips support bursts of up 
+ *  to 128 transfers and the 895A and 896 support bursts of up
+ *  to 64 transfers. All other chips support up to 16 
+ *  transfers bursts.
+ *
+ *  For PCI 32 bit data transfers each transfer is a DWORD.
+ *  It is a QUADWORD (8 bytes) for PCI 64 bit data transfers.
+ *
+ *  We use log base 2 (burst length) as internal code, with 
+ *  value 0 meaning "burst disabled".
+ */
+
+/*
+ *  Burst length from burst code.
+ */
+#define burst_length(bc) (!(bc))? 0 : 1 << (bc)
+
+/*
+ *  Burst code from io register bits.
+ */
+#define burst_code(dmode, ctest4, ctest5) \
+       (ctest4) & 0x80? 0 : (((dmode) & 0xc0) >> 6) + ((ctest5) & 0x04) + 1
+
+/*
+ *  Set initial io register bits from burst code.
+ */
+static __inline void sym_init_burst(hcb_p np, u_char bc)
+{
+       np->rv_ctest4   &= ~0x80;
+       np->rv_dmode    &= ~(0x3 << 6);
+       np->rv_ctest5   &= ~0x4;
+
+       if (!bc) {
+               np->rv_ctest4   |= 0x80;
+       }
+       else {
+               --bc;
+               np->rv_dmode    |= ((bc & 0x3) << 6);
+               np->rv_ctest5   |= (bc & 0x4);
+       }
+}
+
+
+/*
+ * Print out the list of targets that have some flag disabled by user.
+ */
+static void sym_print_targets_flag(hcb_p np, int mask, char *msg)
+{
+       int cnt;
+       int i;
+
+       for (cnt = 0, i = 0 ; i < SYM_CONF_MAX_TARGET ; i++) {
+               if (i == np->myaddr)
+                       continue;
+               if (np->target[i].usrflags & mask) {
+                       if (!cnt++)
+                               printf("%s: %s disabled for targets",
+                                       sym_name(np), msg);
+                       printf(" %d", i);
+               }
+       }
+       if (cnt)
+               printf(".\n");
+}
+
+/*
+ *  Save initial settings of some IO registers.
+ *  Assumed to have been set by BIOS.
+ *  We cannot reset the chip prior to reading the 
+ *  IO registers, since informations will be lost.
+ *  Since the SCRIPTS processor may be running, this 
+ *  is not safe on paper, but it seems to work quite 
+ *  well. :)
+ */
+static void sym_save_initial_setting (hcb_p np)
+{
+       np->sv_scntl0   = INB(nc_scntl0) & 0x0a;
+       np->sv_scntl3   = INB(nc_scntl3) & 0x07;
+       np->sv_dmode    = INB(nc_dmode)  & 0xce;
+       np->sv_dcntl    = INB(nc_dcntl)  & 0xa8;
+       np->sv_ctest3   = INB(nc_ctest3) & 0x01;
+       np->sv_ctest4   = INB(nc_ctest4) & 0x80;
+       np->sv_gpcntl   = INB(nc_gpcntl);
+       np->sv_stest1   = INB(nc_stest1);
+       np->sv_stest2   = INB(nc_stest2) & 0x20;
+       np->sv_stest4   = INB(nc_stest4);
+       if (np->features & FE_C10) {    /* Always large DMA fifo + ultra3 */
+               np->sv_scntl4   = INB(nc_scntl4);
+               np->sv_ctest5   = INB(nc_ctest5) & 0x04;
+       }
+       else
+               np->sv_ctest5   = INB(nc_ctest5) & 0x24;
+}
+
+/*
+ *  Prepare io register values used by sym_start_up() 
+ *  according to selected and supported features.
+ */
+static int sym_prepare_setting(hcb_p np, struct sym_nvram *nvram)
+{
+       u_char  burst_max;
+       u32     period;
+       int i;
+
+       /*
+        *  Wide ?
+        */
+       np->maxwide     = (np->features & FE_WIDE)? 1 : 0;
+
+       /*
+        *  Guess the frequency of the chip's clock.
+        */
+       if      (np->features & (FE_ULTRA3 | FE_ULTRA2))
+               np->clock_khz = 160000;
+       else if (np->features & FE_ULTRA)
+               np->clock_khz = 80000;
+       else
+               np->clock_khz = 40000;
+
+       /*
+        *  Get the clock multiplier factor.
+        */
+       if      (np->features & FE_QUAD)
+               np->multiplier  = 4;
+       else if (np->features & FE_DBLR)
+               np->multiplier  = 2;
+       else
+               np->multiplier  = 1;
+
+       /*
+        *  Measure SCSI clock frequency for chips 
+        *  it may vary from assumed one.
+        */
+       if (np->features & FE_VARCLK)
+               sym_getclock(np, np->multiplier);
+
+       /*
+        * Divisor to be used for async (timer pre-scaler).
+        */
+       i = np->clock_divn - 1;
+       while (--i >= 0) {
+               if (10ul * SYM_CONF_MIN_ASYNC * np->clock_khz > div_10M[i]) {
+                       ++i;
+                       break;
+               }
+       }
+       np->rv_scntl3 = i+1;
+
+       /*
+        * The C1010 uses hardwired divisors for async.
+        * So, we just throw away, the async. divisor.:-)
+        */
+       if (np->features & FE_C10)
+               np->rv_scntl3 = 0;
+
+       /*
+        * Minimum synchronous period factor supported by the chip.
+        * Btw, 'period' is in tenths of nanoseconds.
+        */
+       period = (4 * div_10M[0] + np->clock_khz - 1) / np->clock_khz;
+       if      (period <= 250)         np->minsync = 10;
+       else if (period <= 303)         np->minsync = 11;
+       else if (period <= 500)         np->minsync = 12;
+       else                            np->minsync = (period + 40 - 1) / 40;
+
+       /*
+        * Check against chip SCSI standard support (SCSI-2,ULTRA,ULTRA2).
+        */
+       if      (np->minsync < 25 &&
+                !(np->features & (FE_ULTRA|FE_ULTRA2|FE_ULTRA3)))
+               np->minsync = 25;
+       else if (np->minsync < 12 &&
+                !(np->features & (FE_ULTRA2|FE_ULTRA3)))
+               np->minsync = 12;
+
+       /*
+        * Maximum synchronous period factor supported by the chip.
+        */
+       period = (11 * div_10M[np->clock_divn - 1]) / (4 * np->clock_khz);
+       np->maxsync = period > 2540 ? 254 : period / 10;
+
+       /*
+        * If chip is a C1010, guess the sync limits in DT mode.
+        */
+       if ((np->features & (FE_C10|FE_ULTRA3)) == (FE_C10|FE_ULTRA3)) {
+               if (np->clock_khz == 160000) {
+                       np->minsync_dt = 9;
+                       np->maxsync_dt = 50;
+                       np->maxoffs_dt = nvram->type ? 62 : 31;
+               }
+       }
+       
+       /*
+        *  64 bit addressing  (895A/896/1010) ?
+        */
+       if (np->features & FE_DAC) {
+#if   SYM_CONF_DMA_ADDRESSING_MODE == 0
+               np->rv_ccntl1   |= (DDAC);
+#elif SYM_CONF_DMA_ADDRESSING_MODE == 1
+               if (!np->use_dac)
+                       np->rv_ccntl1   |= (DDAC);
+               else
+                       np->rv_ccntl1   |= (XTIMOD | EXTIBMV);
+#elif SYM_CONF_DMA_ADDRESSING_MODE == 2
+               if (!np->use_dac)
+                       np->rv_ccntl1   |= (DDAC);
+               else
+                       np->rv_ccntl1   |= (0 | EXTIBMV);
+#endif
+       }
+
+       /*
+        *  Phase mismatch handled by SCRIPTS (895A/896/1010) ?
+        */
+       if (np->features & FE_NOPM)
+               np->rv_ccntl0   |= (ENPMJ);
+
+       /*
+        *  C1010-33 Errata: Part Number:609-039638 (rev. 1) is fixed.
+        *  In dual channel mode, contention occurs if internal cycles
+        *  are used. Disable internal cycles.
+        */
+       if (np->device_id == PCI_ID_LSI53C1010 &&
+           np->revision_id < 0x1)
+               np->rv_ccntl0   |=  DILS;
+
+       /*
+        *  Select burst length (dwords)
+        */
+       burst_max       = SYM_SETUP_BURST_ORDER;
+       if (burst_max == 255)
+               burst_max = burst_code(np->sv_dmode, np->sv_ctest4,
+                                      np->sv_ctest5);
+       if (burst_max > 7)
+               burst_max = 7;
+       if (burst_max > np->maxburst)
+               burst_max = np->maxburst;
+
+       /*
+        *  DEL 352 - 53C810 Rev x11 - Part Number 609-0392140 - ITEM 2.
+        *  This chip and the 860 Rev 1 may wrongly use PCI cache line 
+        *  based transactions on LOAD/STORE instructions. So we have 
+        *  to prevent these chips from using such PCI transactions in 
+        *  this driver. The generic ncr driver that does not use 
+        *  LOAD/STORE instructions does not need this work-around.
+        */
+       if ((np->device_id == PCI_ID_SYM53C810 &&
+            np->revision_id >= 0x10 && np->revision_id <= 0x11) ||
+           (np->device_id == PCI_ID_SYM53C860 &&
+            np->revision_id <= 0x1))
+               np->features &= ~(FE_WRIE|FE_ERL|FE_ERMP);
+
+       /*
+        *  Select all supported special features.
+        *  If we are using on-board RAM for scripts, prefetch (PFEN) 
+        *  does not help, but burst op fetch (BOF) does.
+        *  Disabling PFEN makes sure BOF will be used.
+        */
+       if (np->features & FE_ERL)
+               np->rv_dmode    |= ERL;         /* Enable Read Line */
+       if (np->features & FE_BOF)
+               np->rv_dmode    |= BOF;         /* Burst Opcode Fetch */
+       if (np->features & FE_ERMP)
+               np->rv_dmode    |= ERMP;        /* Enable Read Multiple */
+#if 1
+       if ((np->features & FE_PFEN) && !np->ram_ba)
+#else
+       if (np->features & FE_PFEN)
+#endif
+               np->rv_dcntl    |= PFEN;        /* Prefetch Enable */
+       if (np->features & FE_CLSE)
+               np->rv_dcntl    |= CLSE;        /* Cache Line Size Enable */
+       if (np->features & FE_WRIE)
+               np->rv_ctest3   |= WRIE;        /* Write and Invalidate */
+       if (np->features & FE_DFS)
+               np->rv_ctest5   |= DFS;         /* Dma Fifo Size */
+
+       /*
+        *  Select some other
+        */
+       if (SYM_SETUP_PCI_PARITY)
+               np->rv_ctest4   |= MPEE; /* Master parity checking */
+       if (SYM_SETUP_SCSI_PARITY)
+               np->rv_scntl0   |= 0x0a; /*  full arb., ena parity, par->ATN  */
+
+       /*
+        *  Get parity checking, host ID and verbose mode from NVRAM
+        */
+       np->myaddr = 255;
+       sym_nvram_setup_host (np, nvram);
+
+       /*
+        *  Get SCSI addr of host adapter (set by bios?).
+        */
+       if (np->myaddr == 255) {
+               np->myaddr = INB(nc_scid) & 0x07;
+               if (!np->myaddr)
+                       np->myaddr = SYM_SETUP_HOST_ID;
+       }
+
+       /*
+        *  Prepare initial io register bits for burst length
+        */
+       sym_init_burst(np, burst_max);
+
+       /*
+        *  Set SCSI BUS mode.
+        *  - LVD capable chips (895/895A/896/1010) report the 
+        *    current BUS mode through the STEST4 IO register.
+        *  - For previous generation chips (825/825A/875), 
+        *    user has to tell us how to check against HVD, 
+        *    since a 100% safe algorithm is not possible.
+        */
+       np->scsi_mode = SMODE_SE;
+       if (np->features & (FE_ULTRA2|FE_ULTRA3))
+               np->scsi_mode = (np->sv_stest4 & SMODE);
+       else if (np->features & FE_DIFF) {
+               if (SYM_SETUP_SCSI_DIFF == 1) {
+                       if (np->sv_scntl3) {
+                               if (np->sv_stest2 & 0x20)
+                                       np->scsi_mode = SMODE_HVD;
+                       }
+                       else if (nvram->type == SYM_SYMBIOS_NVRAM) {
+                               if (!(INB(nc_gpreg) & 0x08))
+                                       np->scsi_mode = SMODE_HVD;
+                       }
+               }
+               else if (SYM_SETUP_SCSI_DIFF == 2)
+                       np->scsi_mode = SMODE_HVD;
+       }
+       if (np->scsi_mode == SMODE_HVD)
+               np->rv_stest2 |= 0x20;
+
+       /*
+        *  Set LED support from SCRIPTS.
+        *  Ignore this feature for boards known to use a 
+        *  specific GPIO wiring and for the 895A, 896 
+        *  and 1010 that drive the LED directly.
+        */
+       if ((SYM_SETUP_SCSI_LED || 
+            (nvram->type == SYM_SYMBIOS_NVRAM ||
+             (nvram->type == SYM_TEKRAM_NVRAM &&
+              np->device_id == PCI_ID_SYM53C895))) &&
+           !(np->features & FE_LEDC) && !(np->sv_gpcntl & 0x01))
+               np->features |= FE_LED0;
+
+       /*
+        *  Set irq mode.
+        */
+       switch(SYM_SETUP_IRQ_MODE & 3) {
+       case 2:
+               np->rv_dcntl    |= IRQM;
+               break;
+       case 1:
+               np->rv_dcntl    |= (np->sv_dcntl & IRQM);
+               break;
+       default:
+               break;
+       }
+
+       /*
+        *  Configure targets according to driver setup.
+        *  If NVRAM present get targets setup from NVRAM.
+        */
+       for (i = 0 ; i < SYM_CONF_MAX_TARGET ; i++) {
+               tcb_p tp = &np->target[i];
+
+               tp->tinfo.user.scsi_version = tp->tinfo.curr.scsi_version= 2;
+               tp->tinfo.user.spi_version  = tp->tinfo.curr.spi_version = 2;
+               tp->tinfo.user.period = np->minsync;
+               tp->tinfo.user.offset = np->maxoffs;
+               tp->tinfo.user.width  = np->maxwide ? BUS_16_BIT : BUS_8_BIT;
+               tp->usrflags |= (SYM_DISC_ENABLED | SYM_TAGS_ENABLED);
+               tp->usrtags = SYM_SETUP_MAX_TAG;
+
+               sym_nvram_setup_target (np, i, nvram);
+
+               /*
+                *  For now, guess PPR/DT support from the period 
+                *  and BUS width.
+                */
+               if (np->features & FE_ULTRA3) {
+                       if (tp->tinfo.user.period <= 9  &&
+                           tp->tinfo.user.width == BUS_16_BIT) {
+                               tp->tinfo.user.options |= PPR_OPT_DT;
+                               tp->tinfo.user.offset   = np->maxoffs_dt;
+                               tp->tinfo.user.spi_version = 3;
+                       }
+               }
+
+               if (!tp->usrtags)
+                       tp->usrflags &= ~SYM_TAGS_ENABLED;
+       }
+
+       /*
+        *  Let user know about the settings.
+        */
+       i = nvram->type;
+       printf("%s: %s NVRAM, ID %d, Fast-%d, %s, %s\n", sym_name(np),
+               i  == SYM_SYMBIOS_NVRAM ? "Symbios" :
+               (i == SYM_TEKRAM_NVRAM  ? "Tekram" : "No"),
+               np->myaddr,
+               (np->features & FE_ULTRA3) ? 80 : 
+               (np->features & FE_ULTRA2) ? 40 : 
+               (np->features & FE_ULTRA)  ? 20 : 10,
+               sym_scsi_bus_mode(np->scsi_mode),
+               (np->rv_scntl0 & 0xa)   ? "parity checking" : "NO parity");
+       /*
+        *  Tell him more on demand.
+        */
+       if (sym_verbose) {
+               printf("%s: %s IRQ line driver%s\n",
+                       sym_name(np),
+                       np->rv_dcntl & IRQM ? "totem pole" : "open drain",
+                       np->ram_ba ? ", using on-chip SRAM" : "");
+               printf("%s: using %s firmware.\n", sym_name(np), np->fw_name);
+               if (np->features & FE_NOPM)
+                       printf("%s: handling phase mismatch from SCRIPTS.\n", 
+                              sym_name(np));
+       }
+       /*
+        *  And still more.
+        */
+       if (sym_verbose >= 2) {
+               printf ("%s: initial SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+                       "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+                       sym_name(np), np->sv_scntl3, np->sv_dmode, np->sv_dcntl,
+                       np->sv_ctest3, np->sv_ctest4, np->sv_ctest5);
+
+               printf ("%s: final   SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+                       "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+                       sym_name(np), np->rv_scntl3, np->rv_dmode, np->rv_dcntl,
+                       np->rv_ctest3, np->rv_ctest4, np->rv_ctest5);
+       }
+       /*
+        *  Let user be aware of targets that have some disable flags set.
+        */
+       sym_print_targets_flag(np, SYM_SCAN_BOOT_DISABLED, "SCAN AT BOOT");
+       if (sym_verbose)
+               sym_print_targets_flag(np, SYM_SCAN_LUNS_DISABLED,
+                                      "SCAN FOR LUNS");
+
+       return 0;
+}
+
+/*
+ *  Test the pci bus snoop logic :-(
+ *
+ *  Has to be called with interrupts disabled.
+ */
+#ifndef SYM_CONF_IOMAPPED
+static int sym_regtest (hcb_p np)
+{
+       register volatile u32 data;
+       /*
+        *  chip registers may NOT be cached.
+        *  write 0xffffffff to a read only register area,
+        *  and try to read it back.
+        */
+       data = 0xffffffff;
+       OUTL_OFF(offsetof(struct sym_reg, nc_dstat), data);
+       data = INL_OFF(offsetof(struct sym_reg, nc_dstat));
+#if 1
+       if (data == 0xffffffff) {
+#else
+       if ((data & 0xe2f0fffd) != 0x02000080) {
+#endif
+               printf ("CACHE TEST FAILED: reg dstat-sstat2 readback %x.\n",
+                       (unsigned) data);
+               return (0x10);
+       };
+       return (0);
+}
+#endif
+
+static int sym_snooptest (hcb_p np)
+{
+       u32     sym_rd, sym_wr, sym_bk, host_rd, host_wr, pc, dstat;
+       int     i, err=0;
+#ifndef SYM_CONF_IOMAPPED
+       err |= sym_regtest (np);
+       if (err) return (err);
+#endif
+restart_test:
+       /*
+        *  Enable Master Parity Checking as we intend 
+        *  to enable it for normal operations.
+        */
+       OUTB (nc_ctest4, (np->rv_ctest4 & MPEE));
+       /*
+        *  init
+        */
+       pc  = SCRIPTZ_BA (np, snooptest);
+       host_wr = 1;
+       sym_wr  = 2;
+       /*
+        *  Set memory and register.
+        */
+       np->scratch = cpu_to_scr(host_wr);
+       OUTL (nc_temp, sym_wr);
+       /*
+        *  Start script (exchange values)
+        */
+       OUTL (nc_dsa, np->hcb_ba);
+       OUTL_DSP (pc);
+       /*
+        *  Wait 'til done (with timeout)
+        */
+       for (i=0; i<SYM_SNOOP_TIMEOUT; i++)
+               if (INB(nc_istat) & (INTF|SIP|DIP))
+                       break;
+       if (i>=SYM_SNOOP_TIMEOUT) {
+               printf ("CACHE TEST FAILED: timeout.\n");
+               return (0x20);
+       };
+       /*
+        *  Check for fatal DMA errors.
+        */
+       dstat = INB (nc_dstat);
+#if 1  /* Band aiding for broken hardwares that fail PCI parity */
+       if ((dstat & MDPE) && (np->rv_ctest4 & MPEE)) {
+               printf ("%s: PCI DATA PARITY ERROR DETECTED - "
+                       "DISABLING MASTER DATA PARITY CHECKING.\n",
+                       sym_name(np));
+               np->rv_ctest4 &= ~MPEE;
+               goto restart_test;
+       }
+#endif
+       if (dstat & (MDPE|BF|IID)) {
+               printf ("CACHE TEST FAILED: DMA error (dstat=0x%02x).", dstat);
+               return (0x80);
+       }
+       /*
+        *  Save termination position.
+        */
+       pc = INL (nc_dsp);
+       /*
+        *  Read memory and register.
+        */
+       host_rd = scr_to_cpu(np->scratch);
+       sym_rd  = INL (nc_scratcha);
+       sym_bk  = INL (nc_temp);
+       /*
+        *  Check termination position.
+        */
+       if (pc != SCRIPTZ_BA (np, snoopend)+8) {
+               printf ("CACHE TEST FAILED: script execution failed.\n");
+               printf ("start=%08lx, pc=%08lx, end=%08lx\n", 
+                       (u_long) SCRIPTZ_BA (np, snooptest), (u_long) pc,
+                       (u_long) SCRIPTZ_BA (np, snoopend) +8);
+               return (0x40);
+       };
+       /*
+        *  Show results.
+        */
+       if (host_wr != sym_rd) {
+               printf ("CACHE TEST FAILED: host wrote %d, chip read %d.\n",
+                       (int) host_wr, (int) sym_rd);
+               err |= 1;
+       };
+       if (host_rd != sym_wr) {
+               printf ("CACHE TEST FAILED: chip wrote %d, host read %d.\n",
+                       (int) sym_wr, (int) host_rd);
+               err |= 2;
+       };
+       if (sym_bk != sym_wr) {
+               printf ("CACHE TEST FAILED: chip wrote %d, read back %d.\n",
+                       (int) sym_wr, (int) sym_bk);
+               err |= 4;
+       };
+
+       return (err);
+}
+
+/*
+ *  log message for real hard errors
+ *
+ *  sym0 targ 0?: ERROR (ds:si) (so-si-sd) (sx/s3/s4) @ name (dsp:dbc).
+ *           reg: r0 r1 r2 r3 r4 r5 r6 ..... rf.
+ *
+ *  exception register:
+ *     ds:     dstat
+ *     si:     sist
+ *
+ *  SCSI bus lines:
+ *     so:     control lines as driven by chip.
+ *     si:     control lines as seen by chip.
+ *     sd:     scsi data lines as seen by chip.
+ *
+ *  wide/fastmode:
+ *     sx:     sxfer  (see the manual)
+ *     s3:     scntl3 (see the manual)
+ *     s4:     scntl4 (see the manual)
+ *
+ *  current script command:
+ *     dsp:    script adress (relative to start of script).
+ *     dbc:    first word of script command.
+ *
+ *  First 24 register of the chip:
+ *     r0..rf
+ */
+static void sym_log_hard_error(hcb_p np, u_short sist, u_char dstat)
+{
+       u32     dsp;
+       int     script_ofs;
+       int     script_size;
+       char    *script_name;
+       u_char  *script_base;
+       int     i;
+
+       dsp     = INL (nc_dsp);
+
+       if      (dsp > np->scripta_ba &&
+                dsp <= np->scripta_ba + np->scripta_sz) {
+               script_ofs      = dsp - np->scripta_ba;
+               script_size     = np->scripta_sz;
+               script_base     = (u_char *) np->scripta0;
+               script_name     = "scripta";
+       }
+       else if (np->scriptb_ba < dsp && 
+                dsp <= np->scriptb_ba + np->scriptb_sz) {
+               script_ofs      = dsp - np->scriptb_ba;
+               script_size     = np->scriptb_sz;
+               script_base     = (u_char *) np->scriptb0;
+               script_name     = "scriptb";
+       } else {
+               script_ofs      = dsp;
+               script_size     = 0;
+               script_base     = 0;
+               script_name     = "mem";
+       }
+
+       printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x/%x) @ (%s %x:%08x).\n",
+               sym_name (np), (unsigned)INB (nc_sdid)&0x0f, dstat, sist,
+               (unsigned)INB (nc_socl),   (unsigned)INB (nc_sbcl),
+               (unsigned)INB (nc_sbdl),   (unsigned)INB (nc_sxfer),
+               (unsigned)INB (nc_scntl3),
+               (np->features & FE_C10) ?  (unsigned)INB (nc_scntl4) : 0,
+               script_name, script_ofs,   (unsigned)INL (nc_dbc));
+
+       if (((script_ofs & 3) == 0) &&
+           (unsigned)script_ofs < script_size) {
+               printf ("%s: script cmd = %08x\n", sym_name(np),
+                       scr_to_cpu((int) *(u32 *)(script_base + script_ofs)));
+       }
+
+        printf ("%s: regdump:", sym_name(np));
+        for (i=0; i<24;i++)
+            printf (" %02x", (unsigned)INB_OFF(i));
+        printf (".\n");
+
+       /*
+        *  PCI BUS error.
+        */
+       if (dstat & (MDPE|BF))
+               sym_log_bus_error(np);
+}
+
+static struct sym_pci_chip sym_pci_dev_table[] = {
+ {PCI_ID_SYM53C810, 0x0f, "810", 4, 8, 4, 64,
+ FE_ERL}
+ ,
+#ifdef SYM_DEBUG_GENERIC_SUPPORT
+ {PCI_ID_SYM53C810, 0xff, "810a", 4,  8, 4, 1,
+ FE_BOF}
+ ,
+#else
+ {PCI_ID_SYM53C810, 0xff, "810a", 4,  8, 4, 1,
+ FE_CACHE_SET|FE_LDSTR|FE_PFEN|FE_BOF}
+ ,
+#endif
+ {PCI_ID_SYM53C815, 0xff, "815", 4,  8, 4, 64,
+ FE_BOF|FE_ERL}
+ ,
+ {PCI_ID_SYM53C825, 0x0f, "825", 6,  8, 4, 64,
+ FE_WIDE|FE_BOF|FE_ERL|FE_DIFF}
+ ,
+ {PCI_ID_SYM53C825, 0xff, "825a", 6,  8, 4, 2,
+ FE_WIDE|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM|FE_DIFF}
+ ,
+ {PCI_ID_SYM53C860, 0xff, "860", 4,  8, 5, 1,
+ FE_ULTRA|FE_CACHE_SET|FE_BOF|FE_LDSTR|FE_PFEN}
+ ,
+ {PCI_ID_SYM53C875, 0x01, "875", 6, 16, 5, 2,
+ FE_WIDE|FE_ULTRA|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_DIFF|FE_VARCLK}
+ ,
+ {PCI_ID_SYM53C875, 0xff, "875", 6, 16, 5, 2,
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_DIFF|FE_VARCLK}
+ ,
+ {PCI_ID_SYM53C875_2, 0xff, "875", 6, 16, 5, 2,
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_DIFF|FE_VARCLK}
+ ,
+ {PCI_ID_SYM53C885, 0xff, "885", 6, 16, 5, 2,
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_DIFF|FE_VARCLK}
+ ,
+#ifdef SYM_DEBUG_GENERIC_SUPPORT
+ {PCI_ID_SYM53C895, 0xff, "895", 6, 31, 7, 2,
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|
+ FE_RAM|FE_LCKFRQ}
+ ,
+#else
+ {PCI_ID_SYM53C895, 0xff, "895", 6, 31, 7, 2,
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_LCKFRQ}
+ ,
+#endif
+ {PCI_ID_SYM53C896, 0xff, "896", 6, 31, 7, 4,
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_RAM8K|FE_64BIT|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_LCKFRQ}
+ ,
+ {PCI_ID_SYM53C895A, 0xff, "895a", 6, 31, 7, 4,
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_RAM8K|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_LCKFRQ}
+ ,
+ {PCI_ID_SYM53C875A, 0xff, "875a", 6, 31, 7, 4,
+ FE_WIDE|FE_ULTRA|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_LCKFRQ}
+ ,
+ {PCI_ID_LSI53C1010, 0x00, "1010-33", 6, 31, 7, 8,
+ FE_WIDE|FE_ULTRA3|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFBC|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_RAM8K|FE_64BIT|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_CRC|
+ FE_C10}
+ ,
+ {PCI_ID_LSI53C1010, 0xff, "1010-33", 6, 31, 7, 8,
+ FE_WIDE|FE_ULTRA3|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFBC|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_RAM8K|FE_64BIT|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_CRC|
+ FE_C10|FE_U3EN}
+ ,
+ {PCI_ID_LSI53C1010_2, 0xff, "1010-66", 6, 31, 7, 8,
+ FE_WIDE|FE_ULTRA3|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFBC|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_RAM8K|FE_64BIT|FE_DAC|FE_IO256|FE_NOPM|FE_LEDC|FE_66MHZ|FE_CRC|
+ FE_C10|FE_U3EN}
+ ,
+ {PCI_ID_LSI53C1510D, 0xff, "1510d", 6, 31, 7, 4,
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|
+ FE_RAM|FE_IO256|FE_LEDC}
+};
+
+#define sym_pci_num_devs \
+       (sizeof(sym_pci_dev_table) / sizeof(sym_pci_dev_table[0]))
+
+/*
+ *  Look up the chip table.
+ *
+ *  Return a pointer to the chip entry if found, 
+ *  zero otherwise.
+ */
+struct sym_pci_chip *
+sym_lookup_pci_chip_table (u_short device_id, u_char revision)
+{
+       struct  sym_pci_chip *chip;
+       int     i;
+
+       for (i = 0; i < sym_pci_num_devs; i++) {
+               chip = &sym_pci_dev_table[i];
+               if (device_id != chip->device_id)
+                       continue;
+               if (revision > chip->revision_id)
+                       continue;
+               return chip;
+       }
+
+       return 0;
+}
+
+#if SYM_CONF_DMA_ADDRESSING_MODE == 2
+/*
+ *  Lookup the 64 bit DMA segments map.
+ *  This is only used if the direct mapping 
+ *  has been unsuccessful.
+ */
+int sym_lookup_dmap(hcb_p np, u32 h, int s)
+{
+       int i;
+
+       if (!np->use_dac)
+               goto weird;
+
+       /* Look up existing mappings */
+       for (i = SYM_DMAP_SIZE-1; i > 0; i--) {
+               if (h == np->dmap_bah[i])
+                       return i;
+       }
+       /* If direct mapping is free, get it */
+       if (!np->dmap_bah[s])
+               goto new;
+       /* Collision -> lookup free mappings */
+       for (s = SYM_DMAP_SIZE-1; s > 0; s--) {
+               if (!np->dmap_bah[s])
+                       goto new;
+       }
+weird:
+       panic("sym: ran out of 64 bit DMA segment registers");
+       return -1;
+new:
+       np->dmap_bah[s] = h;
+       np->dmap_dirty = 1;
+       return s;
+}
+
+/*
+ *  Update IO registers scratch C..R so they will be 
+ *  in sync. with queued CCB expectations.
+ */
+static void sym_update_dmap_regs(hcb_p np)
+{
+       int o, i;
+
+       if (!np->dmap_dirty)
+               return;
+       o = offsetof(struct sym_reg, nc_scrx[0]);
+       for (i = 0; i < SYM_DMAP_SIZE; i++) {
+               OUTL_OFF(o, np->dmap_bah[i]);
+               o += 4;
+       }
+       np->dmap_dirty = 0;
+}
+#endif
+
+/*
+ *  Prepare the next negotiation message if needed.
+ *
+ *  Fill in the part of message buffer that contains the 
+ *  negotiation and the nego_status field of the CCB.
+ *  Returns the size of the message in bytes.
+ */
+static int sym_prepare_nego(hcb_p np, ccb_p cp, int nego, u_char *msgptr)
+{
+       tcb_p tp = &np->target[cp->target];
+       int msglen = 0;
+
+       /*
+        *  Early C1010 chips need a work-around for DT 
+        *  data transfer to work.
+        */
+       if (!(np->features & FE_U3EN))
+               tp->tinfo.goal.options = 0;
+       /*
+        *  negotiate using PPR ?
+        */
+       if (tp->tinfo.goal.options & PPR_OPT_MASK)
+               nego = NS_PPR;
+       /*
+        *  negotiate wide transfers ?
+        */
+       else if (tp->tinfo.curr.width != tp->tinfo.goal.width)
+               nego = NS_WIDE;
+       /*
+        *  negotiate synchronous transfers?
+        */
+       else if (tp->tinfo.curr.period != tp->tinfo.goal.period ||
+                tp->tinfo.curr.offset != tp->tinfo.goal.offset)
+               nego = NS_SYNC;
+
+       switch (nego) {
+       case NS_SYNC:
+               msgptr[msglen++] = M_EXTENDED;
+               msgptr[msglen++] = 3;
+               msgptr[msglen++] = M_X_SYNC_REQ;
+               msgptr[msglen++] = tp->tinfo.goal.period;
+               msgptr[msglen++] = tp->tinfo.goal.offset;
+               break;
+       case NS_WIDE:
+               msgptr[msglen++] = M_EXTENDED;
+               msgptr[msglen++] = 2;
+               msgptr[msglen++] = M_X_WIDE_REQ;
+               msgptr[msglen++] = tp->tinfo.goal.width;
+               break;
+       case NS_PPR:
+               msgptr[msglen++] = M_EXTENDED;
+               msgptr[msglen++] = 6;
+               msgptr[msglen++] = M_X_PPR_REQ;
+               msgptr[msglen++] = tp->tinfo.goal.period;
+               msgptr[msglen++] = 0;
+               msgptr[msglen++] = tp->tinfo.goal.offset;
+               msgptr[msglen++] = tp->tinfo.goal.width;
+               msgptr[msglen++] = tp->tinfo.goal.options & PPR_OPT_DT;
+               break;
+       };
+
+       cp->nego_status = nego;
+
+       if (nego) {
+               tp->nego_cp = cp; /* Keep track a nego will be performed */
+               if (DEBUG_FLAGS & DEBUG_NEGO) {
+                       sym_print_nego_msg(np, cp->target, 
+                                         nego == NS_SYNC ? "sync msgout" :
+                                         nego == NS_WIDE ? "wide msgout" :
+                                         "ppr msgout", msgptr);
+               };
+       };
+
+       return msglen;
+}
+
+/*
+ *  Insert a job into the start queue.
+ */
+void sym_put_start_queue(hcb_p np, ccb_p cp)
+{
+       u_short qidx;
+
+#ifdef SYM_CONF_IARB_SUPPORT
+       /*
+        *  If the previously queued CCB is not yet done, 
+        *  set the IARB hint. The SCRIPTS will go with IARB 
+        *  for this job when starting the previous one.
+        *  We leave devices a chance to win arbitration by 
+        *  not using more than 'iarb_max' consecutive 
+        *  immediate arbitrations.
+        */
+       if (np->last_cp && np->iarb_count < np->iarb_max) {
+               np->last_cp->host_flags |= HF_HINT_IARB;
+               ++np->iarb_count;
+       }
+       else
+               np->iarb_count = 0;
+       np->last_cp = cp;
+#endif
+
+#if   SYM_CONF_DMA_ADDRESSING_MODE == 2
+       /*
+        *  Make SCRIPTS aware of the 64 bit DMA 
+        *  segment registers not being up-to-date.
+        */
+       if (np->dmap_dirty)
+               cp->host_xflags |= HX_DMAP_DIRTY;
+#endif
+
+       /*
+        *  Optionnaly, set the IO timeout condition.
+        */
+#ifdef SYM_OPT_HANDLE_IO_TIMEOUT
+       sym_timeout_ccb(np, cp, sym_cam_timeout(cp->cam_ccb));
+#endif
+
+       /*
+        *  Insert first the idle task and then our job.
+        *  The MBs should ensure proper ordering.
+        */
+       qidx = np->squeueput + 2;
+       if (qidx >= MAX_QUEUE*2) qidx = 0;
+
+       np->squeue [qidx]          = cpu_to_scr(np->idletask_ba);
+       MEMORY_WRITE_BARRIER();
+       np->squeue [np->squeueput] = cpu_to_scr(cp->ccb_ba);
+
+       np->squeueput = qidx;
+
+       if (DEBUG_FLAGS & DEBUG_QUEUE)
+               printf ("%s: queuepos=%d.\n", sym_name (np), np->squeueput);
+
+       /*
+        *  Script processor may be waiting for reselect.
+        *  Wake it up.
+        */
+       MEMORY_WRITE_BARRIER();
+       OUTB (nc_istat, SIGP|np->istat_sem);
+}
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+/*
+ *  Start next ready-to-start CCBs.
+ */
+void sym_start_next_ccbs(hcb_p np, lcb_p lp, int maxn)
+{
+       SYM_QUEHEAD *qp;
+       ccb_p cp;
+
+       /* 
+        *  Paranoia, as usual. :-)
+        */
+       assert(!lp->started_tags || !lp->started_no_tag);
+
+       /*
+        *  Try to start as many commands as asked by caller.
+        *  Prevent from having both tagged and untagged 
+        *  commands queued to the device at the same time.
+        */
+       while (maxn--) {
+               qp = sym_remque_head(&lp->waiting_ccbq);
+               if (!qp)
+                       break;
+               cp = sym_que_entry(qp, struct sym_ccb, link2_ccbq);
+               if (cp->tag != NO_TAG) {
+                       if (lp->started_no_tag ||
+                           lp->started_tags >= lp->started_max) {
+                               sym_insque_head(qp, &lp->waiting_ccbq);
+                               break;
+                       }
+                       lp->itlq_tbl[cp->tag] = cpu_to_scr(cp->ccb_ba);
+                       lp->head.resel_sa =
+                               cpu_to_scr(SCRIPTA_BA (np, resel_tag));
+                       ++lp->started_tags;
+               } else {
+                       if (lp->started_no_tag || lp->started_tags) {
+                               sym_insque_head(qp, &lp->waiting_ccbq);
+                               break;
+                       }
+                       lp->head.itl_task_sa = cpu_to_scr(cp->ccb_ba);
+                       lp->head.resel_sa =
+                             cpu_to_scr(SCRIPTA_BA (np, resel_no_tag));
+                       ++lp->started_no_tag;
+               }
+               cp->started = 1;
+               sym_insque_tail(qp, &lp->started_ccbq);
+               sym_put_start_queue(np, cp);
+       }
+}
+#endif /* SYM_OPT_HANDLE_DEVICE_QUEUEING */
+
+/*
+ *  The chip may have completed jobs. Look at the DONE QUEUE.
+ *
+ *  On paper, memory read barriers may be needed here to 
+ *  prevent out of order LOADs by the CPU from having 
+ *  prefetched stale data prior to DMA having occurred.
+ */
+static int sym_wakeup_done (hcb_p np)
+{
+       ccb_p cp;
+       int i, n;
+       u32 dsa;
+
+       n = 0;
+       i = np->dqueueget;
+
+       /* MEMORY_READ_BARRIER(); */
+       while (1) {
+               dsa = scr_to_cpu(np->dqueue[i]);
+               if (!dsa)
+                       break;
+               np->dqueue[i] = 0;
+               if ((i = i+2) >= MAX_QUEUE*2)
+                       i = 0;
+
+               cp = sym_ccb_from_dsa(np, dsa);
+               if (cp) {
+                       MEMORY_READ_BARRIER();
+                       sym_complete_ok (np, cp);
+                       ++n;
+               }
+               else
+                       printf ("%s: bad DSA (%x) in done queue.\n",
+                               sym_name(np), (u_int) dsa);
+       }
+       np->dqueueget = i;
+
+       return n;
+}
+
+/*
+ *  Complete all active CCBs with error.
+ *  Used on CHIP/SCSI RESET.
+ */
+static void sym_flush_busy_queue (hcb_p np, int cam_status)
+{
+       /*
+        *  Move all active CCBs to the COMP queue 
+        *  and flush this queue.
+        */
+       sym_que_splice(&np->busy_ccbq, &np->comp_ccbq);
+       sym_que_init(&np->busy_ccbq);
+       sym_flush_comp_queue(np, cam_status);
+}
+
+/*
+ *  Start chip.
+ *
+ *  'reason' means:
+ *     0: initialisation.
+ *     1: SCSI BUS RESET delivered or received.
+ *     2: SCSI BUS MODE changed.
+ */
+void sym_start_up (hcb_p np, int reason)
+{
+       int     i;
+       u32     phys;
+
+       /*
+        *  Reset chip if asked, otherwise just clear fifos.
+        */
+       if (reason == 1)
+               sym_soft_reset(np);
+       else {
+               OUTB (nc_stest3, TE|CSF);
+               OUTONB (nc_ctest3, CLF);
+       }
+       /*
+        *  Clear Start Queue
+        */
+       phys = np->squeue_ba;
+       for (i = 0; i < MAX_QUEUE*2; i += 2) {
+               np->squeue[i]   = cpu_to_scr(np->idletask_ba);
+               np->squeue[i+1] = cpu_to_scr(phys + (i+2)*4);
+       }
+       np->squeue[MAX_QUEUE*2-1] = cpu_to_scr(phys);
+
+       /*
+        *  Start at first entry.
+        */
+       np->squeueput = 0;
+
+       /*
+        *  Clear Done Queue
+        */
+       phys = np->dqueue_ba;
+       for (i = 0; i < MAX_QUEUE*2; i += 2) {
+               np->dqueue[i]   = 0;
+               np->dqueue[i+1] = cpu_to_scr(phys + (i+2)*4);
+       }
+       np->dqueue[MAX_QUEUE*2-1] = cpu_to_scr(phys);
+
+       /*
+        *  Start at first entry.
+        */
+       np->dqueueget = 0;
+
+       /*
+        *  Install patches in scripts.
+        *  This also let point to first position the start 
+        *  and done queue pointers used from SCRIPTS.
+        */
+       np->fw_patch(np);
+
+       /*
+        *  Wakeup all pending jobs.
+        */
+       sym_flush_busy_queue(np, CAM_SCSI_BUS_RESET);
+
+       /*
+        *  Init chip.
+        */
+       OUTB (nc_istat,  0x00   );      /*  Remove Reset, abort */
+       UDELAY (2000);  /* The 895 needs time for the bus mode to settle */
+
+       OUTB (nc_scntl0, np->rv_scntl0 | 0xc0);
+                                       /*  full arb., ena parity, par->ATN  */
+       OUTB (nc_scntl1, 0x00);         /*  odd parity, and remove CRST!! */
+
+       sym_selectclock(np, np->rv_scntl3);     /* Select SCSI clock */
+
+       OUTB (nc_scid  , RRE|np->myaddr);       /* Adapter SCSI address */
+       OUTW (nc_respid, 1ul<<np->myaddr);      /* Id to respond to */
+       OUTB (nc_istat , SIGP   );              /*  Signal Process */
+       OUTB (nc_dmode , np->rv_dmode);         /* Burst length, dma mode */
+       OUTB (nc_ctest5, np->rv_ctest5);        /* Large fifo + large burst */
+
+       OUTB (nc_dcntl , NOCOM|np->rv_dcntl);   /* Protect SFBR */
+       OUTB (nc_ctest3, np->rv_ctest3);        /* Write and invalidate */
+       OUTB (nc_ctest4, np->rv_ctest4);        /* Master parity checking */
+
+       /* Extended Sreq/Sack filtering not supported on the C10 */
+       if (np->features & FE_C10)
+               OUTB (nc_stest2, np->rv_stest2);
+       else
+               OUTB (nc_stest2, EXT|np->rv_stest2);
+
+       OUTB (nc_stest3, TE);                   /* TolerANT enable */
+       OUTB (nc_stime0, 0x0c);                 /* HTH disabled  STO 0.25 sec */
+
+       /*
+        *  For now, disable AIP generation on C1010-66.
+        */
+       if (np->device_id == PCI_ID_LSI53C1010_2)
+               OUTB (nc_aipcntl1, DISAIP);
+
+       /*
+        *  C10101 rev. 0 errata.
+        *  Errant SGE's when in narrow. Write bits 4 & 5 of
+        *  STEST1 register to disable SGE. We probably should do 
+        *  that from SCRIPTS for each selection/reselection, but 
+        *  I just don't want. :)
+        */
+       if (np->device_id == PCI_ID_LSI53C1010 &&
+           np->revision_id < 1)
+               OUTB (nc_stest1, INB(nc_stest1) | 0x30);
+
+       /*
+        *  DEL 441 - 53C876 Rev 5 - Part Number 609-0392787/2788 - ITEM 2.
+        *  Disable overlapped arbitration for some dual function devices, 
+        *  regardless revision id (kind of post-chip-design feature. ;-))
+        */
+       if (np->device_id == PCI_ID_SYM53C875)
+               OUTB (nc_ctest0, (1<<5));
+       else if (np->device_id == PCI_ID_SYM53C896)
+               np->rv_ccntl0 |= DPR;
+
+       /*
+        *  Write CCNTL0/CCNTL1 for chips capable of 64 bit addressing 
+        *  and/or hardware phase mismatch, since only such chips 
+        *  seem to support those IO registers.
+        */
+       if (np->features & (FE_DAC|FE_NOPM)) {
+               OUTB (nc_ccntl0, np->rv_ccntl0);
+               OUTB (nc_ccntl1, np->rv_ccntl1);
+       }
+
+#if    SYM_CONF_DMA_ADDRESSING_MODE == 2
+       /*
+        *  Set up scratch C and DRS IO registers to map the 32 bit 
+        *  DMA address range our data structures are located in.
+        */
+       if (np->use_dac) {
+               np->dmap_bah[0] = 0;    /* ??? */
+               OUTL (nc_scrx[0], np->dmap_bah[0]);
+               OUTL (nc_drs, np->dmap_bah[0]);
+       }
+#endif
+
+       /*
+        *  If phase mismatch handled by scripts (895A/896/1010),
+        *  set PM jump addresses.
+        */
+       if (np->features & FE_NOPM) {
+               OUTL (nc_pmjad1, SCRIPTB_BA (np, pm_handle));
+               OUTL (nc_pmjad2, SCRIPTB_BA (np, pm_handle));
+       }
+
+       /*
+        *    Enable GPIO0 pin for writing if LED support from SCRIPTS.
+        *    Also set GPIO5 and clear GPIO6 if hardware LED control.
+        */
+       if (np->features & FE_LED0)
+               OUTB(nc_gpcntl, INB(nc_gpcntl) & ~0x01);
+       else if (np->features & FE_LEDC)
+               OUTB(nc_gpcntl, (INB(nc_gpcntl) & ~0x41) | 0x20);
+
+       /*
+        *      enable ints
+        */
+       OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST|PAR);
+       OUTB (nc_dien , MDPE|BF|SSI|SIR|IID);
+
+       /*
+        *  For 895/6 enable SBMC interrupt and save current SCSI bus mode.
+        *  Try to eat the spurious SBMC interrupt that may occur when 
+        *  we reset the chip but not the SCSI BUS (at initialization).
+        */
+       if (np->features & (FE_ULTRA2|FE_ULTRA3)) {
+               OUTONW (nc_sien, SBMC);
+               if (reason == 0) {
+                       MDELAY(100);
+                       INW (nc_sist);
+               }
+               np->scsi_mode = INB (nc_stest4) & SMODE;
+       }
+
+       /*
+        *  Fill in target structure.
+        *  Reinitialize usrsync.
+        *  Reinitialize usrwide.
+        *  Prepare sync negotiation according to actual SCSI bus mode.
+        */
+       for (i=0;i<SYM_CONF_MAX_TARGET;i++) {
+               tcb_p tp = &np->target[i];
+
+               tp->to_reset  = 0;
+               tp->head.sval = 0;
+               tp->head.wval = np->rv_scntl3;
+               tp->head.uval = 0;
+
+               tp->tinfo.curr.period = 0;
+               tp->tinfo.curr.offset = 0;
+               tp->tinfo.curr.width  = BUS_8_BIT;
+               tp->tinfo.curr.options = 0;
+       }
+
+       /*
+        *  Download SCSI SCRIPTS to on-chip RAM if present,
+        *  and start script processor.
+        *  We do the download preferently from the CPU.
+        *  For platforms that may not support PCI memory mapping,
+        *  we use simple SCRIPTS that performs MEMORY MOVEs.
+        */
+       if (np->ram_ba) {
+               if (sym_verbose >= 2)
+                       printf ("%s: Downloading SCSI SCRIPTS.\n",
+                               sym_name(np));
+#ifdef SYM_OPT_NO_BUS_MEMORY_MAPPING
+               np->fw_patch(np);
+               if (np->ram_ws == 8192)
+                       phys = SCRIPTZ_BA (np, start_ram64);
+               else
+                       phys = SCRIPTZ_BA (np, start_ram);
+#else
+               if (np->ram_ws == 8192) {
+                       OUTRAM_OFF(4096, np->scriptb0, np->scriptb_sz);
+                       phys =  scr_to_cpu(np->scr_ram_seg);
+                       OUTL (nc_mmws, phys);
+                       OUTL (nc_mmrs, phys);
+                       OUTL (nc_sfs,  phys);
+                       phys = SCRIPTB_BA (np, start64);
+               }
+               else
+                       phys = SCRIPTA_BA (np, init);
+               OUTRAM_OFF(0, np->scripta0, np->scripta_sz);
+#endif
+       }
+       else
+               phys = SCRIPTA_BA (np, init);
+
+       np->istat_sem = 0;
+
+       OUTL (nc_dsa, np->hcb_ba);
+       OUTL_DSP (phys);
+
+       /*
+        *  Notify the XPT about the RESET condition.
+        */
+       if (reason != 0)
+               sym_xpt_async_bus_reset(np);
+}
+
+/*
+ *  Switch trans mode for current job and it's target.
+ */
+static void sym_settrans(hcb_p np, int target, u_char dt, u_char ofs,
+                        u_char per, u_char wide, u_char div, u_char fak)
+{
+       SYM_QUEHEAD *qp;
+       u_char sval, wval, uval;
+       tcb_p tp = &np->target[target];
+
+       assert(target == (INB (nc_sdid) & 0x0f));
+
+       sval = tp->head.sval;
+       wval = tp->head.wval;
+       uval = tp->head.uval;
+
+#if 0
+       printf("XXXX sval=%x wval=%x uval=%x (%x)\n", 
+               sval, wval, uval, np->rv_scntl3);
+#endif
+       /*
+        *  Set the offset.
+        */
+       if (!(np->features & FE_C10))
+               sval = (sval & ~0x1f) | ofs;
+       else
+               sval = (sval & ~0x3f) | ofs;
+
+       /*
+        *  Set the sync divisor and extra clock factor.
+        */
+       if (ofs != 0) {
+               wval = (wval & ~0x70) | ((div+1) << 4);
+               if (!(np->features & FE_C10))
+                       sval = (sval & ~0xe0) | (fak << 5);
+               else {
+                       uval = uval & ~(XCLKH_ST|XCLKH_DT|XCLKS_ST|XCLKS_DT);
+                       if (fak >= 1) uval |= (XCLKH_ST|XCLKH_DT);
+                       if (fak >= 2) uval |= (XCLKS_ST|XCLKS_DT);
+               }
+       }
+
+       /*
+        *  Set the bus width.
+        */
+       wval = wval & ~EWS;
+       if (wide != 0)
+               wval |= EWS;
+
+       /*
+        *  Set misc. ultra enable bits.
+        */
+       if (np->features & FE_C10) {
+               uval = uval & ~(U3EN|AIPCKEN);
+               if (dt) {
+                       assert(np->features & FE_U3EN);
+                       uval |= U3EN;
+               }
+       }
+       else {
+               wval = wval & ~ULTRA;
+               if (per <= 12)  wval |= ULTRA;
+       }
+
+       /*
+        *   Stop there if sync parameters are unchanged.
+        */
+       if (tp->head.sval == sval && 
+           tp->head.wval == wval &&
+           tp->head.uval == uval)
+               return;
+       tp->head.sval = sval;
+       tp->head.wval = wval;
+       tp->head.uval = uval;
+
+       /*
+        *  Disable extended Sreq/Sack filtering if per < 50.
+        *  Not supported on the C1010.
+        */
+       if (per < 50 && !(np->features & FE_C10))
+               OUTOFFB (nc_stest2, EXT);
+
+       /*
+        *  set actual value and sync_status
+        */
+       OUTB (nc_sxfer,  tp->head.sval);
+       OUTB (nc_scntl3, tp->head.wval);
+
+       if (np->features & FE_C10) {
+               OUTB (nc_scntl4, tp->head.uval);
+       }
+
+       /*
+        *  patch ALL busy ccbs of this target.
+        */
+       FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
+               ccb_p cp;
+               cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+               if (cp->target != target)
+                       continue;
+               cp->phys.select.sel_scntl3 = tp->head.wval;
+               cp->phys.select.sel_sxfer  = tp->head.sval;
+               if (np->features & FE_C10) {
+                       cp->phys.select.sel_scntl4 = tp->head.uval;
+               }
+       }
+}
+
+/*
+ *  We received a WDTR.
+ *  Let everything be aware of the changes.
+ */
+static void sym_setwide(hcb_p np, int target, u_char wide)
+{
+       tcb_p tp = &np->target[target];
+
+       sym_settrans(np, target, 0, 0, 0, wide, 0, 0);
+
+       tp->tinfo.goal.width = tp->tinfo.curr.width = wide;
+       tp->tinfo.curr.offset = 0;
+       tp->tinfo.curr.period = 0;
+       tp->tinfo.curr.options = 0;
+
+       sym_xpt_async_nego_wide(np, target);
+}
+
+/*
+ *  We received a SDTR.
+ *  Let everything be aware of the changes.
+ */
+static void
+sym_setsync(hcb_p np, int target,
+            u_char ofs, u_char per, u_char div, u_char fak)
+{
+       tcb_p tp = &np->target[target];
+       u_char wide = (tp->head.wval & EWS) ? BUS_16_BIT : BUS_8_BIT;
+
+       sym_settrans(np, target, 0, ofs, per, wide, div, fak);
+
+       tp->tinfo.goal.period   = tp->tinfo.curr.period  = per;
+       tp->tinfo.goal.offset   = tp->tinfo.curr.offset  = ofs;
+       tp->tinfo.goal.options  = tp->tinfo.curr.options = 0;
+
+       sym_xpt_async_nego_sync(np, target);
+}
+
+/*
+ *  We received a PPR.
+ *  Let everything be aware of the changes.
+ */
+static void 
+sym_setpprot(hcb_p np, int target, u_char dt, u_char ofs,
+             u_char per, u_char wide, u_char div, u_char fak)
+{
+       tcb_p tp = &np->target[target];
+
+       sym_settrans(np, target, dt, ofs, per, wide, div, fak);
+
+       tp->tinfo.goal.width    = tp->tinfo.curr.width  = wide;
+       tp->tinfo.goal.period   = tp->tinfo.curr.period = per;
+       tp->tinfo.goal.offset   = tp->tinfo.curr.offset = ofs;
+       tp->tinfo.goal.options  = tp->tinfo.curr.options = dt;
+
+       sym_xpt_async_nego_ppr(np, target);
+}
+
+/*
+ *  generic recovery from scsi interrupt
+ *
+ *  The doc says that when the chip gets an SCSI interrupt,
+ *  it tries to stop in an orderly fashion, by completing 
+ *  an instruction fetch that had started or by flushing 
+ *  the DMA fifo for a write to memory that was executing.
+ *  Such a fashion is not enough to know if the instruction 
+ *  that was just before the current DSP value has been 
+ *  executed or not.
+ *
+ *  There are some small SCRIPTS sections that deal with 
+ *  the start queue and the done queue that may break any 
+ *  assomption from the C code if we are interrupted 
+ *  inside, so we reset if this happens. Btw, since these 
+ *  SCRIPTS sections are executed while the SCRIPTS hasn't 
+ *  started SCSI operations, it is very unlikely to happen.
+ *
+ *  All the driver data structures are supposed to be 
+ *  allocated from the same 4 GB memory window, so there 
+ *  is a 1 to 1 relationship between DSA and driver data 
+ *  structures. Since we are careful :) to invalidate the 
+ *  DSA when we complete a command or when the SCRIPTS 
+ *  pushes a DSA into a queue, we can trust it when it 
+ *  points to a CCB.
+ */
+static void sym_recover_scsi_int (hcb_p np, u_char hsts)
+{
+       u32     dsp     = INL (nc_dsp);
+       u32     dsa     = INL (nc_dsa);
+       ccb_p cp        = sym_ccb_from_dsa(np, dsa);
+
+       /*
+        *  If we haven't been interrupted inside the SCRIPTS 
+        *  critical pathes, we can safely restart the SCRIPTS 
+        *  and trust the DSA value if it matches a CCB.
+        */
+       if ((!(dsp > SCRIPTA_BA (np, getjob_begin) &&
+              dsp < SCRIPTA_BA (np, getjob_end) + 1)) &&
+           (!(dsp > SCRIPTA_BA (np, ungetjob) &&
+              dsp < SCRIPTA_BA (np, reselect) + 1)) &&
+           (!(dsp > SCRIPTB_BA (np, sel_for_abort) &&
+              dsp < SCRIPTB_BA (np, sel_for_abort_1) + 1)) &&
+           (!(dsp > SCRIPTA_BA (np, done) &&
+              dsp < SCRIPTA_BA (np, done_end) + 1))) {
+               OUTB (nc_ctest3, np->rv_ctest3 | CLF);  /* clear dma fifo  */
+               OUTB (nc_stest3, TE|CSF);               /* clear scsi fifo */
+               /*
+                *  If we have a CCB, let the SCRIPTS call us back for 
+                *  the handling of the error with SCRATCHA filled with 
+                *  STARTPOS. This way, we will be able to freeze the 
+                *  device queue and requeue awaiting IOs.
+                */
+               if (cp) {
+                       cp->host_status = hsts;
+                       OUTL_DSP (SCRIPTA_BA (np, complete_error));
+               }
+               /*
+                *  Otherwise just restart the SCRIPTS.
+                */
+               else {
+                       OUTL (nc_dsa, 0xffffff);
+                       OUTL_DSP (SCRIPTA_BA (np, start));
+               }
+       }
+       else
+               goto reset_all;
+
+       return;
+
+reset_all:
+       sym_start_reset(np);
+}
+
+/*
+ *  chip exception handler for selection timeout
+ */
+static void sym_int_sto (hcb_p np)
+{
+       u32 dsp = INL (nc_dsp);
+
+       if (DEBUG_FLAGS & DEBUG_TINY) printf ("T");
+
+       if (dsp == SCRIPTA_BA (np, wf_sel_done) + 8)
+               sym_recover_scsi_int(np, HS_SEL_TIMEOUT);
+       else
+               sym_start_reset(np);
+}
+
+/*
+ *  chip exception handler for unexpected disconnect
+ */
+static void sym_int_udc (hcb_p np)
+{
+       printf ("%s: unexpected disconnect\n", sym_name(np));
+       sym_recover_scsi_int(np, HS_UNEXPECTED);
+}
+
+/*
+ *  chip exception handler for SCSI bus mode change
+ *
+ *  spi2-r12 11.2.3 says a transceiver mode change must 
+ *  generate a reset event and a device that detects a reset 
+ *  event shall initiate a hard reset. It says also that a
+ *  device that detects a mode change shall set data transfer 
+ *  mode to eight bit asynchronous, etc...
+ *  So, just reinitializing all except chip should be enough.
+ */
+static void sym_int_sbmc (hcb_p np)
+{
+       u_char scsi_mode = INB (nc_stest4) & SMODE;
+
+       /*
+        *  Notify user.
+        */
+       printf("%s: SCSI BUS mode change from %s to %s.\n", sym_name(np),
+               sym_scsi_bus_mode(np->scsi_mode), sym_scsi_bus_mode(scsi_mode));
+
+       /*
+        *  Should suspend command processing for a few seconds and 
+        *  reinitialize all except the chip.
+        */
+       sym_start_up (np, 2);
+}
+
+/*
+ *  chip exception handler for SCSI parity error.
+ *
+ *  When the chip detects a SCSI parity error and is 
+ *  currently executing a (CH)MOV instruction, it does 
+ *  not interrupt immediately, but tries to finish the 
+ *  transfer of the current scatter entry before 
+ *  interrupting. The following situations may occur:
+ *
+ *  - The complete scatter entry has been transferred 
+ *    without the device having changed phase.
+ *    The chip will then interrupt with the DSP pointing 
+ *    to the instruction that follows the MOV.
+ *
+ *  - A phase mismatch occurs before the MOV finished 
+ *    and phase errors are to be handled by the C code.
+ *    The chip will then interrupt with both PAR and MA 
+ *    conditions set.
+ *
+ *  - A phase mismatch occurs before the MOV finished and 
+ *    phase errors are to be handled by SCRIPTS.
+ *    The chip will load the DSP with the phase mismatch 
+ *    JUMP address and interrupt the host processor.
+ */
+static void sym_int_par (hcb_p np, u_short sist)
+{
+       u_char  hsts    = INB (HS_PRT);
+       u32     dsp     = INL (nc_dsp);
+       u32     dbc     = INL (nc_dbc);
+       u32     dsa     = INL (nc_dsa);
+       u_char  sbcl    = INB (nc_sbcl);
+       u_char  cmd     = dbc >> 24;
+       int phase       = cmd & 7;
+       ccb_p   cp      = sym_ccb_from_dsa(np, dsa);
+
+       printf("%s: SCSI parity error detected: SCR1=%d DBC=%x SBCL=%x\n",
+               sym_name(np), hsts, dbc, sbcl);
+
+       /*
+        *  Check that the chip is connected to the SCSI BUS.
+        */
+       if (!(INB (nc_scntl1) & ISCON)) {
+               sym_recover_scsi_int(np, HS_UNEXPECTED);
+               return;
+       }
+
+       /*
+        *  If the nexus is not clearly identified, reset the bus.
+        *  We will try to do better later.
+        */
+       if (!cp)
+               goto reset_all;
+
+       /*
+        *  Check instruction was a MOV, direction was INPUT and 
+        *  ATN is asserted.
+        */
+       if ((cmd & 0xc0) || !(phase & 1) || !(sbcl & 0x8))
+               goto reset_all;
+
+       /*
+        *  Keep track of the parity error.
+        */
+       OUTONB (HF_PRT, HF_EXT_ERR);
+       cp->xerr_status |= XE_PARITY_ERR;
+
+       /*
+        *  Prepare the message to send to the device.
+        */
+       np->msgout[0] = (phase == 7) ? M_PARITY : M_ID_ERROR;
+
+       /*
+        *  If the old phase was DATA IN phase, we have to deal with
+        *  the 3 situations described above.
+        *  For other input phases (MSG IN and STATUS), the device 
+        *  must resend the whole thing that failed parity checking 
+        *  or signal error. So, jumping to dispatcher should be OK.
+        */
+       if (phase == 1 || phase == 5) {
+               /* Phase mismatch handled by SCRIPTS */
+               if (dsp == SCRIPTB_BA (np, pm_handle))
+                       OUTL_DSP (dsp);
+               /* Phase mismatch handled by the C code */
+               else if (sist & MA)
+                       sym_int_ma (np);
+               /* No phase mismatch occurred */
+               else {
+                       sym_set_script_dp (np, cp, dsp);
+                       OUTL_DSP (SCRIPTA_BA (np, dispatch));
+               }
+       }
+       else if (phase == 7)    /* We definitely cannot handle parity errors */
+#if 1                          /* in message-in phase due to the relection  */
+               goto reset_all; /* path and various message anticipations.   */
+#else
+               OUTL_DSP (SCRIPTA_BA (np, clrack));
+#endif
+       else
+               OUTL_DSP (SCRIPTA_BA (np, dispatch));
+       return;
+
+reset_all:
+       sym_start_reset(np);
+       return;
+}
+
+/*
+ *  chip exception handler for phase errors.
+ *
+ *  We have to construct a new transfer descriptor,
+ *  to transfer the rest of the current block.
+ */
+static void sym_int_ma (hcb_p np)
+{
+       u32     dbc;
+       u32     rest;
+       u32     dsp;
+       u32     dsa;
+       u32     nxtdsp;
+       u32     *vdsp;
+       u32     oadr, olen;
+       u32     *tblp;
+        u32    newcmd;
+       u_int   delta;
+       u_char  cmd;
+       u_char  hflags, hflags0;
+       struct  sym_pmc *pm;
+       ccb_p   cp;
+
+       dsp     = INL (nc_dsp);
+       dbc     = INL (nc_dbc);
+       dsa     = INL (nc_dsa);
+
+       cmd     = dbc >> 24;
+       rest    = dbc & 0xffffff;
+       delta   = 0;
+
+       /*
+        *  locate matching cp if any.
+        */
+       cp = sym_ccb_from_dsa(np, dsa);
+
+       /*
+        *  Donnot take into account dma fifo and various buffers in 
+        *  INPUT phase since the chip flushes everything before 
+        *  raising the MA interrupt for interrupted INPUT phases.
+        *  For DATA IN phase, we will check for the SWIDE later.
+        */
+       if ((cmd & 7) != 1 && (cmd & 7) != 5) {
+               u_char ss0, ss2;
+
+               if (np->features & FE_DFBC)
+                       delta = INW (nc_dfbc);
+               else {
+                       u32 dfifo;
+
+                       /*
+                        * Read DFIFO, CTEST[4-6] using 1 PCI bus ownership.
+                        */
+                       dfifo = INL(nc_dfifo);
+
+                       /*
+                        *  Calculate remaining bytes in DMA fifo.
+                        *  (CTEST5 = dfifo >> 16)
+                        */
+                       if (dfifo & (DFS << 16))
+                               delta = ((((dfifo >> 8) & 0x300) |
+                                         (dfifo & 0xff)) - rest) & 0x3ff;
+                       else
+                               delta = ((dfifo & 0xff) - rest) & 0x7f;
+               }
+
+               /*
+                *  The data in the dma fifo has not been transfered to
+                *  the target -> add the amount to the rest
+                *  and clear the data.
+                *  Check the sstat2 register in case of wide transfer.
+                */
+               rest += delta;
+               ss0  = INB (nc_sstat0);
+               if (ss0 & OLF) rest++;
+               if (!(np->features & FE_C10))
+                       if (ss0 & ORF) rest++;
+               if (cp && (cp->phys.select.sel_scntl3 & EWS)) {
+                       ss2 = INB (nc_sstat2);
+                       if (ss2 & OLF1) rest++;
+                       if (!(np->features & FE_C10))
+                               if (ss2 & ORF1) rest++;
+               };
+
+               /*
+                *  Clear fifos.
+                */
+               OUTB (nc_ctest3, np->rv_ctest3 | CLF);  /* dma fifo  */
+               OUTB (nc_stest3, TE|CSF);               /* scsi fifo */
+       }
+
+       /*
+        *  log the information
+        */
+       if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE))
+               printf ("P%x%x RL=%d D=%d ", cmd&7, INB(nc_sbcl)&7,
+                       (unsigned) rest, (unsigned) delta);
+
+       /*
+        *  try to find the interrupted script command,
+        *  and the address at which to continue.
+        */
+       vdsp    = 0;
+       nxtdsp  = 0;
+       if      (dsp >  np->scripta_ba &&
+                dsp <= np->scripta_ba + np->scripta_sz) {
+               vdsp = (u32 *)((char*)np->scripta0 + (dsp-np->scripta_ba-8));
+               nxtdsp = dsp;
+       }
+       else if (dsp >  np->scriptb_ba &&
+                dsp <= np->scriptb_ba + np->scriptb_sz) {
+               vdsp = (u32 *)((char*)np->scriptb0 + (dsp-np->scriptb_ba-8));
+               nxtdsp = dsp;
+       }
+
+       /*
+        *  log the information
+        */
+       if (DEBUG_FLAGS & DEBUG_PHASE) {
+               printf ("\nCP=%p DSP=%x NXT=%x VDSP=%p CMD=%x ",
+                       cp, (unsigned)dsp, (unsigned)nxtdsp, vdsp, cmd);
+       };
+
+       if (!vdsp) {
+               printf ("%s: interrupted SCRIPT address not found.\n", 
+                       sym_name (np));
+               goto reset_all;
+       }
+
+       if (!cp) {
+               printf ("%s: SCSI phase error fixup: CCB already dequeued.\n", 
+                       sym_name (np));
+               goto reset_all;
+       }
+
+       /*
+        *  get old startaddress and old length.
+        */
+       oadr = scr_to_cpu(vdsp[1]);
+
+       if (cmd & 0x10) {       /* Table indirect */
+               tblp = (u32 *) ((char*) &cp->phys + oadr);
+               olen = scr_to_cpu(tblp[0]);
+               oadr = scr_to_cpu(tblp[1]);
+       } else {
+               tblp = (u32 *) 0;
+               olen = scr_to_cpu(vdsp[0]) & 0xffffff;
+       };
+
+       if (DEBUG_FLAGS & DEBUG_PHASE) {
+               printf ("OCMD=%x\nTBLP=%p OLEN=%x OADR=%x\n",
+                       (unsigned) (scr_to_cpu(vdsp[0]) >> 24),
+                       tblp,
+                       (unsigned) olen,
+                       (unsigned) oadr);
+       };
+
+       /*
+        *  check cmd against assumed interrupted script command.
+        *  If dt data phase, the MOVE instruction hasn't bit 4 of 
+        *  the phase.
+        */
+       if (((cmd & 2) ? cmd : (cmd & ~4)) != (scr_to_cpu(vdsp[0]) >> 24)) {
+               PRINT_ADDR(cp);
+               printf ("internal error: cmd=%02x != %02x=(vdsp[0] >> 24)\n",
+                       (unsigned)cmd, (unsigned)scr_to_cpu(vdsp[0]) >> 24);
+
+               goto reset_all;
+       };
+
+       /*
+        *  if old phase not dataphase, leave here.
+        */
+       if (cmd & 2) {
+               PRINT_ADDR(cp);
+               printf ("phase change %x-%x %d@%08x resid=%d.\n",
+                       cmd&7, INB(nc_sbcl)&7, (unsigned)olen,
+                       (unsigned)oadr, (unsigned)rest);
+               goto unexpected_phase;
+       };
+
+       /*
+        *  Choose the correct PM save area.
+        *
+        *  Look at the PM_SAVE SCRIPT if you want to understand 
+        *  this stuff. The equivalent code is implemented in 
+        *  SCRIPTS for the 895A, 896 and 1010 that are able to 
+        *  handle PM from the SCRIPTS processor.
+        */
+       hflags0 = INB (HF_PRT);
+       hflags = hflags0;
+
+       if (hflags & (HF_IN_PM0 | HF_IN_PM1 | HF_DP_SAVED)) {
+               if (hflags & HF_IN_PM0)
+                       nxtdsp = scr_to_cpu(cp->phys.pm0.ret);
+               else if (hflags & HF_IN_PM1)
+                       nxtdsp = scr_to_cpu(cp->phys.pm1.ret);
+
+               if (hflags & HF_DP_SAVED)
+                       hflags ^= HF_ACT_PM;
+       }
+
+       if (!(hflags & HF_ACT_PM)) {
+               pm = &cp->phys.pm0;
+               newcmd = SCRIPTA_BA (np, pm0_data);
+       }
+       else {
+               pm = &cp->phys.pm1;
+               newcmd = SCRIPTA_BA (np, pm1_data);
+       }
+
+       hflags &= ~(HF_IN_PM0 | HF_IN_PM1 | HF_DP_SAVED);
+       if (hflags != hflags0)
+               OUTB (HF_PRT, hflags);
+
+       /*
+        *  fillin the phase mismatch context
+        */
+       pm->sg.addr = cpu_to_scr(oadr + olen - rest);
+       pm->sg.size = cpu_to_scr(rest);
+       pm->ret     = cpu_to_scr(nxtdsp);
+
+       /*
+        *  If we have a SWIDE,
+        *  - prepare the address to write the SWIDE from SCRIPTS,
+        *  - compute the SCRIPTS address to restart from,
+        *  - move current data pointer context by one byte.
+        */
+       nxtdsp = SCRIPTA_BA (np, dispatch);
+       if ((cmd & 7) == 1 && cp && (cp->phys.select.sel_scntl3 & EWS) &&
+           (INB (nc_scntl2) & WSR)) {
+               u32 tmp;
+
+               /*
+                *  Set up the table indirect for the MOVE
+                *  of the residual byte and adjust the data 
+                *  pointer context.
+                */
+               tmp = scr_to_cpu(pm->sg.addr);
+               cp->phys.wresid.addr = cpu_to_scr(tmp);
+               pm->sg.addr = cpu_to_scr(tmp + 1);
+               tmp = scr_to_cpu(pm->sg.size);
+               cp->phys.wresid.size = cpu_to_scr((tmp&0xff000000) | 1);
+               pm->sg.size = cpu_to_scr(tmp - 1);
+
+               /*
+                *  If only the residual byte is to be moved, 
+                *  no PM context is needed.
+                */
+               if ((tmp&0xffffff) == 1)
+                       newcmd = pm->ret;
+
+               /*
+                *  Prepare the address of SCRIPTS that will 
+                *  move the residual byte to memory.
+                */
+               nxtdsp = SCRIPTB_BA (np, wsr_ma_helper);
+       }
+
+       if (DEBUG_FLAGS & DEBUG_PHASE) {
+               PRINT_ADDR(cp);
+               printf ("PM %x %x %x / %x %x %x.\n",
+                       hflags0, hflags, newcmd,
+                       (unsigned)scr_to_cpu(pm->sg.addr),
+                       (unsigned)scr_to_cpu(pm->sg.size),
+                       (unsigned)scr_to_cpu(pm->ret));
+       }
+
+       /*
+        *  Restart the SCRIPTS processor.
+        */
+       sym_set_script_dp (np, cp, newcmd);
+       OUTL_DSP (nxtdsp);
+       return;
+
+       /*
+        *  Unexpected phase changes that occurs when the current phase 
+        *  is not a DATA IN or DATA OUT phase are due to error conditions.
+        *  Such event may only happen when the SCRIPTS is using a 
+        *  multibyte SCSI MOVE.
+        *
+        *  Phase change                Some possible cause
+        *
+        *  COMMAND  --> MSG IN SCSI parity error detected by target.
+        *  COMMAND  --> STATUS Bad command or refused by target.
+        *  MSG OUT  --> MSG IN     Message rejected by target.
+        *  MSG OUT  --> COMMAND    Bogus target that discards extended
+        *                      negotiation messages.
+        *
+        *  The code below does not care of the new phase and so 
+        *  trusts the target. Why to annoy it ?
+        *  If the interrupted phase is COMMAND phase, we restart at
+        *  dispatcher.
+        *  If a target does not get all the messages after selection, 
+        *  the code assumes blindly that the target discards extended 
+        *  messages and clears the negotiation status.
+        *  If the target does not want all our response to negotiation,
+        *  we force a SIR_NEGO_PROTO interrupt (it is a hack that avoids 
+        *  bloat for such a should_not_happen situation).
+        *  In all other situation, we reset the BUS.
+        *  Are these assumptions reasonnable ? (Wait and see ...)
+        */
+unexpected_phase:
+       dsp -= 8;
+       nxtdsp = 0;
+
+       switch (cmd & 7) {
+       case 2: /* COMMAND phase */
+               nxtdsp = SCRIPTA_BA (np, dispatch);
+               break;
+#if 0
+       case 3: /* STATUS  phase */
+               nxtdsp = SCRIPTA_BA (np, dispatch);
+               break;
+#endif
+       case 6: /* MSG OUT phase */
+               /*
+                *  If the device may want to use untagged when we want 
+                *  tagged, we prepare an IDENTIFY without disc. granted, 
+                *  since we will not be able to handle reselect.
+                *  Otherwise, we just don't care.
+                */
+               if      (dsp == SCRIPTA_BA (np, send_ident)) {
+                       if (cp->tag != NO_TAG && olen - rest <= 3) {
+                               cp->host_status = HS_BUSY;
+                               np->msgout[0] = M_IDENTIFY | cp->lun;
+                               nxtdsp = SCRIPTB_BA (np, ident_break_atn);
+                       }
+                       else
+                               nxtdsp = SCRIPTB_BA (np, ident_break);
+               }
+               else if (dsp == SCRIPTB_BA (np, send_wdtr) ||
+                        dsp == SCRIPTB_BA (np, send_sdtr) ||
+                        dsp == SCRIPTB_BA (np, send_ppr)) {
+                       nxtdsp = SCRIPTB_BA (np, nego_bad_phase);
+               }
+               break;
+#if 0
+       case 7: /* MSG IN  phase */
+               nxtdsp = SCRIPTA_BA (np, clrack);
+               break;
+#endif
+       }
+
+       if (nxtdsp) {
+               OUTL_DSP (nxtdsp);
+               return;
+       }
+
+reset_all:
+       sym_start_reset(np);
+}
+
+/*
+ *  chip interrupt handler
+ *
+ *  In normal situations, interrupt conditions occur one at 
+ *  a time. But when something bad happens on the SCSI BUS, 
+ *  the chip may raise several interrupt flags before 
+ *  stopping and interrupting the CPU. The additionnal 
+ *  interrupt flags are stacked in some extra registers 
+ *  after the SIP and/or DIP flag has been raised in the 
+ *  ISTAT. After the CPU has read the interrupt condition 
+ *  flag from SIST or DSTAT, the chip unstacks the other 
+ *  interrupt flags and sets the corresponding bits in 
+ *  SIST or DSTAT. Since the chip starts stacking once the 
+ *  SIP or DIP flag is set, there is a small window of time 
+ *  where the stacking does not occur.
+ *
+ *  Typically, multiple interrupt conditions may happen in 
+ *  the following situations:
+ *
+ *  - SCSI parity error + Phase mismatch  (PAR|MA)
+ *    When an parity error is detected in input phase 
+ *    and the device switches to msg-in phase inside a 
+ *    block MOV.
+ *  - SCSI parity error + Unexpected disconnect (PAR|UDC)
+ *    When a stupid device does not want to handle the 
+ *    recovery of an SCSI parity error.
+ *  - Some combinations of STO, PAR, UDC, ...
+ *    When using non compliant SCSI stuff, when user is 
+ *    doing non compliant hot tampering on the BUS, when 
+ *    something really bad happens to a device, etc ...
+ *
+ *  The heuristic suggested by SYMBIOS to handle 
+ *  multiple interrupts is to try unstacking all 
+ *  interrupts conditions and to handle them on some 
+ *  priority based on error severity.
+ *  This will work when the unstacking has been 
+ *  successful, but we cannot be 100 % sure of that, 
+ *  since the CPU may have been faster to unstack than 
+ *  the chip is able to stack. Hmmm ... But it seems that 
+ *  such a situation is very unlikely to happen.
+ *
+ *  If this happen, for example STO caught by the CPU 
+ *  then UDC happenning before the CPU have restarted 
+ *  the SCRIPTS, the driver may wrongly complete the 
+ *  same command on UDC, since the SCRIPTS didn't restart 
+ *  and the DSA still points to the same command.
+ *  We avoid this situation by setting the DSA to an 
+ *  invalid value when the CCB is completed and before 
+ *  restarting the SCRIPTS.
+ *
+ *  Another issue is that we need some section of our 
+ *  recovery procedures to be somehow uninterruptible but 
+ *  the SCRIPTS processor does not provides such a 
+ *  feature. For this reason, we handle recovery preferently 
+ *  from the C code and check against some SCRIPTS critical 
+ *  sections from the C code.
+ *
+ *  Hopefully, the interrupt handling of the driver is now 
+ *  able to resist to weird BUS error conditions, but donnot 
+ *  ask me for any guarantee that it will never fail. :-)
+ *  Use at your own decision and risk.
+ */
+
+void sym_interrupt (hcb_p np)
+{
+       u_char  istat, istatc;
+       u_char  dstat;
+       u_short sist;
+
+       /*
+        *  interrupt on the fly ?
+        *  (SCRIPTS may still be running)
+        *
+        *  A `dummy read' is needed to ensure that the 
+        *  clear of the INTF flag reaches the device 
+        *  and that posted writes are flushed to memory
+        *  before the scanning of the DONE queue.
+        *  Note that SCRIPTS also (dummy) read to memory 
+        *  prior to deliver the INTF interrupt condition.
+        */
+       istat = INB (nc_istat);
+       if (istat & INTF) {
+               OUTB (nc_istat, (istat & SIGP) | INTF | np->istat_sem);
+               istat = INB (nc_istat);         /* DUMMY READ */
+               if (DEBUG_FLAGS & DEBUG_TINY) printf ("F ");
+               (void)sym_wakeup_done (np);
+       };
+
+       if (!(istat & (SIP|DIP)))
+               return;
+
+#if 0  /* We should never get this one */
+       if (istat & CABRT)
+               OUTB (nc_istat, CABRT);
+#endif
+
+       /*
+        *  PAR and MA interrupts may occur at the same time,
+        *  and we need to know of both in order to handle 
+        *  this situation properly. We try to unstack SCSI 
+        *  interrupts for that reason. BTW, I dislike a LOT 
+        *  such a loop inside the interrupt routine.
+        *  Even if DMA interrupt stacking is very unlikely to 
+        *  happen, we also try unstacking these ones, since 
+        *  this has no performance impact.
+        */
+       sist    = 0;
+       dstat   = 0;
+       istatc  = istat;
+       do {
+               if (istatc & SIP)
+                       sist  |= INW (nc_sist);
+               if (istatc & DIP)
+                       dstat |= INB (nc_dstat);
+               istatc = INB (nc_istat);
+               istat |= istatc;
+       } while (istatc & (SIP|DIP));
+
+       if (DEBUG_FLAGS & DEBUG_TINY)
+               printf ("<%d|%x:%x|%x:%x>",
+                       (int)INB(nc_scr0),
+                       dstat,sist,
+                       (unsigned)INL(nc_dsp),
+                       (unsigned)INL(nc_dbc));
+       /*
+        *  On paper, a memory read barrier may be needed here to 
+        *  prevent out of order LOADs by the CPU from having 
+        *  prefetched stale data prior to DMA having occurred.
+        *  And since we are paranoid ... :)
+        */
+       MEMORY_READ_BARRIER();
+
+       /*
+        *  First, interrupts we want to service cleanly.
+        *
+        *  Phase mismatch (MA) is the most frequent interrupt 
+        *  for chip earlier than the 896 and so we have to service 
+        *  it as quickly as possible.
+        *  A SCSI parity error (PAR) may be combined with a phase 
+        *  mismatch condition (MA).
+        *  Programmed interrupts (SIR) are used to call the C code 
+        *  from SCRIPTS.
+        *  The single step interrupt (SSI) is not used in this 
+        *  driver.
+        */
+       if (!(sist  & (STO|GEN|HTH|SGE|UDC|SBMC|RST)) &&
+           !(dstat & (MDPE|BF|ABRT|IID))) {
+               if      (sist & PAR)    sym_int_par (np, sist);
+               else if (sist & MA)     sym_int_ma (np);
+               else if (dstat & SIR)   sym_int_sir (np);
+               else if (dstat & SSI)   OUTONB_STD ();
+               else                    goto unknown_int;
+               return;
+       };
+
+       /*
+        *  Now, interrupts that donnot happen in normal 
+        *  situations and that we may need to recover from.
+        *
+        *  On SCSI RESET (RST), we reset everything.
+        *  On SCSI BUS MODE CHANGE (SBMC), we complete all 
+        *  active CCBs with RESET status, prepare all devices 
+        *  for negotiating again and restart the SCRIPTS.
+        *  On STO and UDC, we complete the CCB with the corres- 
+        *  ponding status and restart the SCRIPTS.
+        */
+       if (sist & RST) {
+               printf("%s: SCSI BUS reset detected.\n", sym_name(np));
+               sym_start_up (np, 1);
+               return;
+       };
+
+       OUTB (nc_ctest3, np->rv_ctest3 | CLF);  /* clear dma fifo  */
+       OUTB (nc_stest3, TE|CSF);               /* clear scsi fifo */
+
+       if (!(sist  & (GEN|HTH|SGE)) &&
+           !(dstat & (MDPE|BF|ABRT|IID))) {
+               if      (sist & SBMC)   sym_int_sbmc (np);
+               else if (sist & STO)    sym_int_sto (np);
+               else if (sist & UDC)    sym_int_udc (np);
+               else                    goto unknown_int;
+               return;
+       };
+
+       /*
+        *  Now, interrupts we are not able to recover cleanly.
+        *
+        *  Log message for hard errors.
+        *  Reset everything.
+        */
+
+       sym_log_hard_error(np, sist, dstat);
+
+       if ((sist & (GEN|HTH|SGE)) ||
+               (dstat & (MDPE|BF|ABRT|IID))) {
+               sym_start_reset(np);
+               return;
+       };
+
+unknown_int:
+       /*
+        *  We just miss the cause of the interrupt. :(
+        *  Print a message. The timeout will do the real work.
+        */
+       printf( "%s: unknown interrupt(s) ignored, "
+               "ISTAT=0x%x DSTAT=0x%x SIST=0x%x\n",
+               sym_name(np), istat, dstat, sist);
+}
+
+/*
+ *  Dequeue from the START queue all CCBs that match 
+ *  a given target/lun/task condition (-1 means all),
+ *  and move them from the BUSY queue to the COMP queue 
+ *  with CAM_REQUEUE_REQ status condition.
+ *  This function is used during error handling/recovery.
+ *  It is called with SCRIPTS not running.
+ */
+static int 
+sym_dequeue_from_squeue(hcb_p np, int i, int target, int lun, int task)
+{
+       int j;
+       ccb_p cp;
+
+       /*
+        *  Make sure the starting index is within range.
+        */
+       assert((i >= 0) && (i < 2*MAX_QUEUE));
+
+       /*
+        *  Walk until end of START queue and dequeue every job 
+        *  that matches the target/lun/task condition.
+        */
+       j = i;
+       while (i != np->squeueput) {
+               cp = sym_ccb_from_dsa(np, scr_to_cpu(np->squeue[i]));
+               assert(cp);
+#ifdef SYM_CONF_IARB_SUPPORT
+               /* Forget hints for IARB, they may be no longer relevant */
+               cp->host_flags &= ~HF_HINT_IARB;
+#endif
+               if ((target == -1 || cp->target == target) &&
+                   (lun    == -1 || cp->lun    == lun)    &&
+                   (task   == -1 || cp->tag    == task)) {
+                       sym_set_cam_status(cp->cam_ccb, CAM_REQUEUE_REQ);
+                       sym_remque(&cp->link_ccbq);
+                       sym_insque_tail(&cp->link_ccbq, &np->comp_ccbq);
+               }
+               else {
+                       if (i != j)
+                               np->squeue[j] = np->squeue[i];
+                       if ((j += 2) >= MAX_QUEUE*2) j = 0;
+               }
+               if ((i += 2) >= MAX_QUEUE*2) i = 0;
+       }
+       if (i != j)             /* Copy back the idle task if needed */
+               np->squeue[j] = np->squeue[i];
+       np->squeueput = j;      /* Update our current start queue pointer */
+
+       return (i - j) / 2;
+}
+
+/*
+ *  Complete all CCBs queued to the COMP queue.
+ *
+ *  These CCBs are assumed:
+ *  - Not to be referenced either by devices or 
+ *    SCRIPTS-related queues and datas.
+ *  - To have to be completed with an error condition 
+ *    or requeued.
+ *
+ *  The device queue freeze count is incremented 
+ *  for each CCB that does not prevent this.
+ *  This function is called when all CCBs involved 
+ *  in error handling/recovery have been reaped.
+ */
+void sym_flush_comp_queue(hcb_p np, int cam_status)
+{
+       SYM_QUEHEAD *qp;
+       ccb_p cp;
+
+       while ((qp = sym_remque_head(&np->comp_ccbq)) != 0) {
+               cam_ccb_p ccb;
+               cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+               sym_insque_tail(&cp->link_ccbq, &np->busy_ccbq);
+               /* Leave quiet CCBs waiting for resources */
+               if (cp->host_status == HS_WAIT)
+                       continue;
+               ccb = cp->cam_ccb;
+               if (cam_status)
+                       sym_set_cam_status(ccb, cam_status);
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+               if (sym_get_cam_status(ccb) == CAM_REQUEUE_REQ) {
+                       tcb_p tp = &np->target[cp->target];
+                       lcb_p lp = sym_lp(np, tp, cp->lun);
+                       if (lp) {
+                               sym_remque(&cp->link2_ccbq);
+                               sym_insque_tail(&cp->link2_ccbq,
+                                               &lp->waiting_ccbq);
+                               if (cp->started) {
+                                       if (cp->tag != NO_TAG)
+                                               --lp->started_tags;
+                                       else
+                                               --lp->started_no_tag;
+                               }
+                       }
+                       cp->started = 0;
+                       continue;
+               }
+#endif
+               sym_free_ccb(np, cp);
+               sym_freeze_cam_ccb(ccb);
+               sym_xpt_done(np, ccb);
+       }
+}
+
+/*
+ *  chip handler for bad SCSI status condition
+ *
+ *  In case of bad SCSI status, we unqueue all the tasks 
+ *  currently queued to the controller but not yet started 
+ *  and then restart the SCRIPTS processor immediately.
+ *
+ *  QUEUE FULL and BUSY conditions are handled the same way.
+ *  Basically all the not yet started tasks are requeued in 
+ *  device queue and the queue is frozen until a completion.
+ *
+ *  For CHECK CONDITION and COMMAND TERMINATED status, we use 
+ *  the CCB of the failed command to prepare a REQUEST SENSE 
+ *  SCSI command and queue it to the controller queue.
+ *
+ *  SCRATCHA is assumed to have been loaded with STARTPOS 
+ *  before the SCRIPTS called the C code.
+ */
+static void sym_sir_bad_scsi_status(hcb_p np, int num, ccb_p cp)
+{
+       tcb_p tp        = &np->target[cp->target];
+       u32             startp;
+       u_char          s_status = cp->ssss_status;
+       u_char          h_flags  = cp->host_flags;
+       int             msglen;
+       int             nego;
+       int             i;
+
+       /*
+        *  Compute the index of the next job to start from SCRIPTS.
+        */
+       i = (INL (nc_scratcha) - np->squeue_ba) / 4;
+
+       /*
+        *  The last CCB queued used for IARB hint may be 
+        *  no longer relevant. Forget it.
+        */
+#ifdef SYM_CONF_IARB_SUPPORT
+       if (np->last_cp)
+               np->last_cp = 0;
+#endif
+
+       /*
+        *  Now deal with the SCSI status.
+        */
+       switch(s_status) {
+       case S_BUSY:
+       case S_QUEUE_FULL:
+               if (sym_verbose >= 2) {
+                       PRINT_ADDR(cp);
+                       printf ("%s\n",
+                               s_status == S_BUSY ? "BUSY" : "QUEUE FULL\n");
+               }
+       default:        /* S_INT, S_INT_COND_MET, S_CONFLICT */
+               sym_complete_error (np, cp);
+               break;
+       case S_TERMINATED:
+       case S_CHECK_COND:
+               /*
+                *  If we get an SCSI error when requesting sense, give up.
+                */
+               if (h_flags & HF_SENSE) {
+                       sym_complete_error (np, cp);
+                       break;
+               }
+
+               /*
+                *  Dequeue all queued CCBs for that device not yet started,
+                *  and restart the SCRIPTS processor immediately.
+                */
+               (void) sym_dequeue_from_squeue(np, i, cp->target, cp->lun, -1);
+               OUTL_DSP (SCRIPTA_BA (np, start));
+
+               /*
+                *  Save some info of the actual IO.
+                *  Compute the data residual.
+                */
+               cp->sv_scsi_status = cp->ssss_status;
+               cp->sv_xerr_status = cp->xerr_status;
+               cp->sv_resid = sym_compute_residual(np, cp);
+
+               /*
+                *  Prepare all needed data structures for 
+                *  requesting sense data.
+                */
+
+               /*
+                *  identify message
+                */
+               cp->scsi_smsg2[0] = M_IDENTIFY | cp->lun;
+               msglen = 1;
+
+               /*
+                *  If we are currently using anything different from 
+                *  async. 8 bit data transfers with that target,
+                *  start a negotiation, since the device may want 
+                *  to report us a UNIT ATTENTION condition due to 
+                *  a cause we currently ignore, and we donnot want 
+                *  to be stuck with WIDE and/or SYNC data transfer.
+                *
+                *  cp->nego_status is filled by sym_prepare_nego().
+                */
+               cp->nego_status = 0;
+               nego = 0;
+               if      (tp->tinfo.curr.options & PPR_OPT_MASK)
+                       nego = NS_PPR;
+               else if (tp->tinfo.curr.width != BUS_8_BIT)
+                       nego = NS_WIDE;
+               else if (tp->tinfo.curr.offset != 0)
+                       nego = NS_SYNC;
+               if (nego)
+                       msglen +=
+                       sym_prepare_nego (np,cp, nego, &cp->scsi_smsg2[msglen]);
+               /*
+                *  Message table indirect structure.
+                */
+               cp->phys.smsg.addr      = cpu_to_scr(CCB_BA (cp, scsi_smsg2));
+               cp->phys.smsg.size      = cpu_to_scr(msglen);
+
+               /*
+                *  sense command
+                */
+               cp->phys.cmd.addr       = cpu_to_scr(CCB_BA (cp, sensecmd));
+               cp->phys.cmd.size       = cpu_to_scr(6);
+
+               /*
+                *  patch requested size into sense command
+                */
+               cp->sensecmd[0]         = 0x03;
+               cp->sensecmd[1]         = 0;
+               if (tp->tinfo.curr.scsi_version <= 2 && cp->lun <= 7)
+                       cp->sensecmd[1] = cp->lun << 5;
+               cp->sensecmd[4]         = SYM_SNS_BBUF_LEN;
+               cp->data_len            = SYM_SNS_BBUF_LEN;
+
+               /*
+                *  sense data
+                */
+               bzero(cp->sns_bbuf, SYM_SNS_BBUF_LEN);
+               cp->phys.sense.addr     = cpu_to_scr(vtobus(cp->sns_bbuf));
+               cp->phys.sense.size     = cpu_to_scr(SYM_SNS_BBUF_LEN);
+
+               /*
+                *  requeue the command.
+                */
+               startp = SCRIPTB_BA (np, sdata_in);
+
+               cp->phys.head.savep     = cpu_to_scr(startp);
+               cp->phys.head.lastp     = cpu_to_scr(startp);
+               cp->startp              = cpu_to_scr(startp);
+               cp->goalp               = cpu_to_scr(startp + 16);
+
+               cp->host_xflags = 0;
+               cp->host_status = cp->nego_status ? HS_NEGOTIATE : HS_BUSY;
+               cp->ssss_status = S_ILLEGAL;
+               cp->host_flags  = (HF_SENSE|HF_DATA_IN);
+               cp->xerr_status = 0;
+               cp->extra_bytes = 0;
+
+               cp->phys.head.go.start = cpu_to_scr(SCRIPTA_BA (np, select));
+
+               /*
+                *  Requeue the command.
+                */
+               sym_put_start_queue(np, cp);
+
+               /*
+                *  Give back to upper layer everything we have dequeued.
+                */
+               sym_flush_comp_queue(np, 0);
+               break;
+       }
+}
+
+/*
+ *  After a device has accepted some management message 
+ *  as BUS DEVICE RESET, ABORT TASK, etc ..., or when 
+ *  a device signals a UNIT ATTENTION condition, some 
+ *  tasks are thrown away by the device. We are required 
+ *  to reflect that on our tasks list since the device 
+ *  will never complete these tasks.
+ *
+ *  This function move from the BUSY queue to the COMP 
+ *  queue all disconnected CCBs for a given target that 
+ *  match the following criteria:
+ *  - lun=-1  means any logical UNIT otherwise a given one.
+ *  - task=-1 means any task, otherwise a given one.
+ */
+int sym_clear_tasks(hcb_p np, int cam_status, int target, int lun, int task)
+{
+       SYM_QUEHEAD qtmp, *qp;
+       int i = 0;
+       ccb_p cp;
+
+       /*
+        *  Move the entire BUSY queue to our temporary queue.
+        */
+       sym_que_init(&qtmp);
+       sym_que_splice(&np->busy_ccbq, &qtmp);
+       sym_que_init(&np->busy_ccbq);
+
+       /*
+        *  Put all CCBs that matches our criteria into 
+        *  the COMP queue and put back other ones into 
+        *  the BUSY queue.
+        */
+       while ((qp = sym_remque_head(&qtmp)) != 0) {
+               cam_ccb_p ccb;
+               cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+               ccb = cp->cam_ccb;
+               if (cp->host_status != HS_DISCONNECT ||
+                   cp->target != target             ||
+                   (lun  != -1 && cp->lun != lun)   ||
+                   (task != -1 && 
+                       (cp->tag != NO_TAG && cp->scsi_smsg[2] != task))) {
+                       sym_insque_tail(&cp->link_ccbq, &np->busy_ccbq);
+                       continue;
+               }
+               sym_insque_tail(&cp->link_ccbq, &np->comp_ccbq);
+
+               /* Preserve the software timeout condition */
+               if (sym_get_cam_status(ccb) != CAM_CMD_TIMEOUT)
+                       sym_set_cam_status(ccb, cam_status);
+               ++i;
+#if 0
+printf("XXXX TASK @%p CLEARED\n", cp);
+#endif
+       }
+       return i;
+}
+
+/*
+ *  chip handler for TASKS recovery
+ *
+ *  We cannot safely abort a command, while the SCRIPTS 
+ *  processor is running, since we just would be in race 
+ *  with it.
+ *
+ *  As long as we have tasks to abort, we keep the SEM 
+ *  bit set in the ISTAT. When this bit is set, the 
+ *  SCRIPTS processor interrupts (SIR_SCRIPT_STOPPED) 
+ *  each time it enters the scheduler.
+ *
+ *  If we have to reset a target, clear tasks of a unit,
+ *  or to perform the abort of a disconnected job, we 
+ *  restart the SCRIPTS for selecting the target. Once 
+ *  selected, the SCRIPTS interrupts (SIR_TARGET_SELECTED).
+ *  If it loses arbitration, the SCRIPTS will interrupt again 
+ *  the next time it will enter its scheduler, and so on ...
+ *
+ *  On SIR_TARGET_SELECTED, we scan for the more 
+ *  appropriate thing to do:
+ *
+ *  - If nothing, we just sent a M_ABORT message to the 
+ *    target to get rid of the useless SCSI bus ownership.
+ *    According to the specs, no tasks shall be affected.
+ *  - If the target is to be reset, we send it a M_RESET 
+ *    message.
+ *  - If a logical UNIT is to be cleared , we send the 
+ *    IDENTIFY(lun) + M_ABORT.
+ *  - If an untagged task is to be aborted, we send the 
+ *    IDENTIFY(lun) + M_ABORT.
+ *  - If a tagged task is to be aborted, we send the 
+ *    IDENTIFY(lun) + task attributes + M_ABORT_TAG.
+ *
+ *  Once our 'kiss of death' :) message has been accepted 
+ *  by the target, the SCRIPTS interrupts again 
+ *  (SIR_ABORT_SENT). On this interrupt, we complete 
+ *  all the CCBs that should have been aborted by the 
+ *  target according to our message.
+ */
+static void sym_sir_task_recovery(hcb_p np, int num)
+{
+       SYM_QUEHEAD *qp;
+       ccb_p cp;
+       tcb_p tp;
+       int target=-1, lun=-1, task;
+       int i, k;
+
+       switch(num) {
+       /*
+        *  The SCRIPTS processor stopped before starting
+        *  the next command in order to allow us to perform 
+        *  some task recovery.
+        */
+       case SIR_SCRIPT_STOPPED:
+               /*
+                *  Do we have any target to reset or unit to clear ?
+                */
+               for (i = 0 ; i < SYM_CONF_MAX_TARGET ; i++) {
+                       tp = &np->target[i];
+                       if (tp->to_reset || 
+                           (tp->lun0p && tp->lun0p->to_clear)) {
+                               target = i;
+                               break;
+                       }
+                       if (!tp->lunmp)
+                               continue;
+                       for (k = 1 ; k < SYM_CONF_MAX_LUN ; k++) {
+                               if (tp->lunmp[k] && tp->lunmp[k]->to_clear) {
+                                       target  = i;
+                                       break;
+                               }
+                       }
+                       if (target != -1)
+                               break;
+               }
+
+               /*
+                *  If not, walk the busy queue for any 
+                *  disconnected CCB to be aborted.
+                */
+               if (target == -1) {
+                       FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
+                               cp = sym_que_entry(qp,struct sym_ccb,link_ccbq);
+                               if (cp->host_status != HS_DISCONNECT)
+                                       continue;
+                               if (cp->to_abort) {
+                                       target = cp->target;
+                                       break;
+                               }
+                       }
+               }
+
+               /*
+                *  If some target is to be selected, 
+                *  prepare and start the selection.
+                */
+               if (target != -1) {
+                       tp = &np->target[target];
+                       np->abrt_sel.sel_id     = target;
+                       np->abrt_sel.sel_scntl3 = tp->head.wval;
+                       np->abrt_sel.sel_sxfer  = tp->head.sval;
+                       OUTL(nc_dsa, np->hcb_ba);
+                       OUTL_DSP (SCRIPTB_BA (np, sel_for_abort));
+                       return;
+               }
+
+               /*
+                *  Now look for a CCB to abort that haven't started yet.
+                *  Btw, the SCRIPTS processor is still stopped, so 
+                *  we are not in race.
+                */
+               i = 0;
+               cp = 0;
+               FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
+                       cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+                       if (cp->host_status != HS_BUSY &&
+                           cp->host_status != HS_NEGOTIATE)
+                               continue;
+                       if (!cp->to_abort)
+                               continue;
+#ifdef SYM_CONF_IARB_SUPPORT
+                       /*
+                        *    If we are using IMMEDIATE ARBITRATION, we donnot 
+                        *    want to cancel the last queued CCB, since the 
+                        *    SCRIPTS may have anticipated the selection.
+                        */
+                       if (cp == np->last_cp) {
+                               cp->to_abort = 0;
+                               continue;
+                       }
+#endif
+                       i = 1;  /* Means we have found some */
+                       break;
+               }
+               if (!i) {
+                       /*
+                        *  We are done, so we donnot need 
+                        *  to synchronize with the SCRIPTS anylonger.
+                        *  Remove the SEM flag from the ISTAT.
+                        */
+                       np->istat_sem = 0;
+                       OUTB (nc_istat, SIGP);
+                       break;
+               }
+               /*
+                *  Compute index of next position in the start 
+                *  queue the SCRIPTS intends to start and dequeue 
+                *  all CCBs for that device that haven't been started.
+                */
+               i = (INL (nc_scratcha) - np->squeue_ba) / 4;
+               i = sym_dequeue_from_squeue(np, i, cp->target, cp->lun, -1);
+
+               /*
+                *  Make sure at least our IO to abort has been dequeued.
+                */
+#ifndef SYM_OPT_HANDLE_DEVICE_QUEUEING
+               assert(i && sym_get_cam_status(cp->cam_ccb) == CAM_REQUEUE_REQ);
+#else
+               sym_remque(&cp->link_ccbq);
+               sym_insque_tail(&cp->link_ccbq, &np->comp_ccbq);
+#endif
+               /*
+                *  Keep track in cam status of the reason of the abort.
+                */
+               if (cp->to_abort == 2)
+                       sym_set_cam_status(cp->cam_ccb, CAM_CMD_TIMEOUT);
+               else
+                       sym_set_cam_status(cp->cam_ccb, CAM_REQ_ABORTED);
+
+               /*
+                *  Complete with error everything that we have dequeued.
+                */
+               sym_flush_comp_queue(np, 0);
+               break;
+       /*
+        *  The SCRIPTS processor has selected a target 
+        *  we may have some manual recovery to perform for.
+        */
+       case SIR_TARGET_SELECTED:
+               target = (INB (nc_sdid) & 0xf);
+               tp = &np->target[target];
+
+               np->abrt_tbl.addr = cpu_to_scr(vtobus(np->abrt_msg));
+
+               /*
+                *  If the target is to be reset, prepare a 
+                *  M_RESET message and clear the to_reset flag 
+                *  since we donnot expect this operation to fail.
+                */
+               if (tp->to_reset) {
+                       np->abrt_msg[0] = M_RESET;
+                       np->abrt_tbl.size = 1;
+                       tp->to_reset = 0;
+                       break;
+               }
+
+               /*
+                *  Otherwise, look for some logical unit to be cleared.
+                */
+               if (tp->lun0p && tp->lun0p->to_clear)
+                       lun = 0;
+               else if (tp->lunmp) {
+                       for (k = 1 ; k < SYM_CONF_MAX_LUN ; k++) {
+                               if (tp->lunmp[k] && tp->lunmp[k]->to_clear) {
+                                       lun = k;
+                                       break;
+                               }
+                       }
+               }
+
+               /*
+                *  If a logical unit is to be cleared, prepare 
+                *  an IDENTIFY(lun) + ABORT MESSAGE.
+                */
+               if (lun != -1) {
+                       lcb_p lp = sym_lp(np, tp, lun);
+                       lp->to_clear = 0; /* We donnot expect to fail here */
+                       np->abrt_msg[0] = M_IDENTIFY | lun;
+                       np->abrt_msg[1] = M_ABORT;
+                       np->abrt_tbl.size = 2;
+                       break;
+               }
+
+               /*
+                *  Otherwise, look for some disconnected job to 
+                *  abort for this target.
+                */
+               i = 0;
+               cp = 0;
+               FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
+                       cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+                       if (cp->host_status != HS_DISCONNECT)
+                               continue;
+                       if (cp->target != target)
+                               continue;
+                       if (!cp->to_abort)
+                               continue;
+                       i = 1;  /* Means we have some */
+                       break;
+               }
+
+               /*
+                *  If we have none, probably since the device has 
+                *  completed the command before we won abitration,
+                *  send a M_ABORT message without IDENTIFY.
+                *  According to the specs, the device must just 
+                *  disconnect the BUS and not abort any task.
+                */
+               if (!i) {
+                       np->abrt_msg[0] = M_ABORT;
+                       np->abrt_tbl.size = 1;
+                       break;
+               }
+
+               /*
+                *  We have some task to abort.
+                *  Set the IDENTIFY(lun)
+                */
+               np->abrt_msg[0] = M_IDENTIFY | cp->lun;
+
+               /*
+                *  If we want to abort an untagged command, we 
+                *  will send a IDENTIFY + M_ABORT.
+                *  Otherwise (tagged command), we will send 
+                *  a IDENTITFY + task attributes + ABORT TAG.
+                */
+               if (cp->tag == NO_TAG) {
+                       np->abrt_msg[1] = M_ABORT;
+                       np->abrt_tbl.size = 2;
+               }
+               else {
+                       np->abrt_msg[1] = cp->scsi_smsg[1];
+                       np->abrt_msg[2] = cp->scsi_smsg[2];
+                       np->abrt_msg[3] = M_ABORT_TAG;
+                       np->abrt_tbl.size = 4;
+               }
+               /*
+                *  Keep track of software timeout condition, since the 
+                *  peripheral driver may not count retries on abort 
+                *  conditions not due to timeout.
+                */
+               if (cp->to_abort == 2)
+                       sym_set_cam_status(cp->cam_ccb, CAM_CMD_TIMEOUT);
+               cp->to_abort = 0; /* We donnot expect to fail here */
+               break;
+
+       /*
+        *  The target has accepted our message and switched 
+        *  to BUS FREE phase as we expected.
+        */
+       case SIR_ABORT_SENT:
+               target = (INB (nc_sdid) & 0xf);
+               tp = &np->target[target];
+               
+               /*
+               **  If we didn't abort anything, leave here.
+               */
+               if (np->abrt_msg[0] == M_ABORT)
+                       break;
+
+               /*
+                *  If we sent a M_RESET, then a hardware reset has 
+                *  been performed by the target.
+                *  - Reset everything to async 8 bit
+                *  - Tell ourself to negotiate next time :-)
+                *  - Prepare to clear all disconnected CCBs for 
+                *    this target from our task list (lun=task=-1)
+                */
+               lun = -1;
+               task = -1;
+               if (np->abrt_msg[0] == M_RESET) {
+                       tp->head.sval = 0;
+                       tp->head.wval = np->rv_scntl3;
+                       tp->head.uval = 0;
+                       tp->tinfo.curr.period = 0;
+                       tp->tinfo.curr.offset = 0;
+                       tp->tinfo.curr.width  = BUS_8_BIT;
+                       tp->tinfo.curr.options = 0;
+               }
+
+               /*
+                *  Otherwise, check for the LUN and TASK(s) 
+                *  concerned by the cancelation.
+                *  If it is not ABORT_TAG then it is CLEAR_QUEUE 
+                *  or an ABORT message :-)
+                */
+               else {
+                       lun = np->abrt_msg[0] & 0x3f;
+                       if (np->abrt_msg[1] == M_ABORT_TAG)
+                               task = np->abrt_msg[2];
+               }
+
+               /*
+                *  Complete all the CCBs the device should have 
+                *  aborted due to our 'kiss of death' message.
+                */
+               i = (INL (nc_scratcha) - np->squeue_ba) / 4;
+               (void) sym_dequeue_from_squeue(np, i, target, lun, -1);
+               (void) sym_clear_tasks(np, CAM_REQ_ABORTED, target, lun, task);
+               sym_flush_comp_queue(np, 0);
+
+               /*
+                *  If we sent a BDR, make upper layer aware of that.
+                */
+               if (np->abrt_msg[0] == M_RESET)
+                       sym_xpt_async_sent_bdr(np, target);
+               break;
+       }
+
+       /*
+        *  Print to the log the message we intend to send.
+        */
+       if (num == SIR_TARGET_SELECTED) {
+               PRINT_TARGET(np, target);
+               sym_printl_hex("control msgout:", np->abrt_msg,
+                             np->abrt_tbl.size);
+               np->abrt_tbl.size = cpu_to_scr(np->abrt_tbl.size);
+       }
+
+       /*
+        *  Let the SCRIPTS processor continue.
+        */
+       OUTONB_STD ();
+}
+
+/*
+ *  Gerard's alchemy:) that deals with with the data 
+ *  pointer for both MDP and the residual calculation.
+ *
+ *  I didn't want to bloat the code by more than 200 
+ *  lignes for the handling of both MDP and the residual.
+ *  This has been achieved by using a data pointer 
+ *  representation consisting in an index in the data 
+ *  array (dp_sg) and a negative offset (dp_ofs) that 
+ *  have the following meaning:
+ *
+ *  - dp_sg = SYM_CONF_MAX_SG
+ *    we are at the end of the data script.
+ *  - dp_sg < SYM_CONF_MAX_SG
+ *    dp_sg points to the next entry of the scatter array 
+ *    we want to transfer.
+ *  - dp_ofs < 0
+ *    dp_ofs represents the residual of bytes of the 
+ *    previous entry scatter entry we will send first.
+ *  - dp_ofs = 0
+ *    no residual to send first.
+ *
+ *  The function sym_evaluate_dp() accepts an arbitray 
+ *  offset (basically from the MDP message) and returns 
+ *  the corresponding values of dp_sg and dp_ofs.
+ */
+
+static int sym_evaluate_dp(hcb_p np, ccb_p cp, u32 scr, int *ofs)
+{
+       u32     dp_scr;
+       int     dp_ofs, dp_sg, dp_sgmin;
+       int     tmp;
+       struct sym_pmc *pm;
+
+       /*
+        *  Compute the resulted data pointer in term of a script 
+        *  address within some DATA script and a signed byte offset.
+        */
+       dp_scr = scr;
+       dp_ofs = *ofs;
+       if      (dp_scr == SCRIPTA_BA (np, pm0_data))
+               pm = &cp->phys.pm0;
+       else if (dp_scr == SCRIPTA_BA (np, pm1_data))
+               pm = &cp->phys.pm1;
+       else
+               pm = 0;
+
+       if (pm) {
+               dp_scr  = scr_to_cpu(pm->ret);
+               dp_ofs -= scr_to_cpu(pm->sg.size);
+       }
+
+       /*
+        *  If we are auto-sensing, then we are done.
+        */
+       if (cp->host_flags & HF_SENSE) {
+               *ofs = dp_ofs;
+               return 0;
+       }
+
+       /*
+        *  Deduce the index of the sg entry.
+        *  Keep track of the index of the first valid entry.
+        *  If result is dp_sg = SYM_CONF_MAX_SG, then we are at the 
+        *  end of the data.
+        */
+       tmp = scr_to_cpu(sym_goalp(cp));
+       dp_sg = SYM_CONF_MAX_SG;
+       if (dp_scr != tmp)
+               dp_sg -= (tmp - 8 - (int)dp_scr) / (2*4);
+       dp_sgmin = SYM_CONF_MAX_SG - cp->segments;
+
+       /*
+        *  Move to the sg entry the data pointer belongs to.
+        *
+        *  If we are inside the data area, we expect result to be:
+        *
+        *  Either,
+        *      dp_ofs = 0 and dp_sg is the index of the sg entry
+        *      the data pointer belongs to (or the end of the data)
+        *  Or,
+        *      dp_ofs < 0 and dp_sg is the index of the sg entry 
+        *      the data pointer belongs to + 1.
+        */
+       if (dp_ofs < 0) {
+               int n;
+               while (dp_sg > dp_sgmin) {
+                       --dp_sg;
+                       tmp = scr_to_cpu(cp->phys.data[dp_sg].size);
+                       n = dp_ofs + (tmp & 0xffffff);
+                       if (n > 0) {
+                               ++dp_sg;
+                               break;
+                       }
+                       dp_ofs = n;
+               }
+       }
+       else if (dp_ofs > 0) {
+               while (dp_sg < SYM_CONF_MAX_SG) {
+                       tmp = scr_to_cpu(cp->phys.data[dp_sg].size);
+                       dp_ofs -= (tmp & 0xffffff);
+                       ++dp_sg;
+                       if (dp_ofs <= 0)
+                               break;
+               }
+       }
+
+       /*
+        *  Make sure the data pointer is inside the data area.
+        *  If not, return some error.
+        */
+       if      (dp_sg < dp_sgmin || (dp_sg == dp_sgmin && dp_ofs < 0))
+               goto out_err;
+       else if (dp_sg > SYM_CONF_MAX_SG ||
+                (dp_sg == SYM_CONF_MAX_SG && dp_ofs > 0))
+               goto out_err;
+
+       /*
+        *  Save the extreme pointer if needed.
+        */
+       if (dp_sg > cp->ext_sg ||
+            (dp_sg == cp->ext_sg && dp_ofs > cp->ext_ofs)) {
+               cp->ext_sg  = dp_sg;
+               cp->ext_ofs = dp_ofs;
+       }
+
+       /*
+        *  Return data.
+        */
+       *ofs = dp_ofs;
+       return dp_sg;
+
+out_err:
+       return -1;
+}
+
+/*
+ *  chip handler for MODIFY DATA POINTER MESSAGE
+ *
+ *  We also call this function on IGNORE WIDE RESIDUE 
+ *  messages that do not match a SWIDE full condition.
+ *  Btw, we assume in that situation that such a message 
+ *  is equivalent to a MODIFY DATA POINTER (offset=-1).
+ */
+
+static void sym_modify_dp(hcb_p np, tcb_p tp, ccb_p cp, int ofs)
+{
+       int dp_ofs      = ofs;
+       u32     dp_scr  = sym_get_script_dp (np, cp);
+       u32     dp_ret;
+       u32     tmp;
+       u_char  hflags;
+       int     dp_sg;
+       struct  sym_pmc *pm;
+
+       /*
+        *  Not supported for auto-sense.
+        */
+       if (cp->host_flags & HF_SENSE)
+               goto out_reject;
+
+       /*
+        *  Apply our alchemy:) (see comments in sym_evaluate_dp()), 
+        *  to the resulted data pointer.
+        */
+       dp_sg = sym_evaluate_dp(np, cp, dp_scr, &dp_ofs);
+       if (dp_sg < 0)
+               goto out_reject;
+
+       /*
+        *  And our alchemy:) allows to easily calculate the data 
+        *  script address we want to return for the next data phase.
+        */
+       dp_ret = cpu_to_scr(sym_goalp(cp));
+       dp_ret = dp_ret - 8 - (SYM_CONF_MAX_SG - dp_sg) * (2*4);
+
+       /*
+        *  If offset / scatter entry is zero we donnot need 
+        *  a context for the new current data pointer.
+        */
+       if (dp_ofs == 0) {
+               dp_scr = dp_ret;
+               goto out_ok;
+       }
+
+       /*
+        *  Get a context for the new current data pointer.
+        */
+       hflags = INB (HF_PRT);
+
+       if (hflags & HF_DP_SAVED)
+               hflags ^= HF_ACT_PM;
+
+       if (!(hflags & HF_ACT_PM)) {
+               pm  = &cp->phys.pm0;
+               dp_scr = SCRIPTA_BA (np, pm0_data);
+       }
+       else {
+               pm = &cp->phys.pm1;
+               dp_scr = SCRIPTA_BA (np, pm1_data);
+       }
+
+       hflags &= ~(HF_DP_SAVED);
+
+       OUTB (HF_PRT, hflags);
+
+       /*
+        *  Set up the new current data pointer.
+        *  ofs < 0 there, and for the next data phase, we 
+        *  want to transfer part of the data of the sg entry 
+        *  corresponding to index dp_sg-1 prior to returning 
+        *  to the main data script.
+        */
+       pm->ret = cpu_to_scr(dp_ret);
+       tmp  = scr_to_cpu(cp->phys.data[dp_sg-1].addr);
+       tmp += scr_to_cpu(cp->phys.data[dp_sg-1].size) + dp_ofs;
+       pm->sg.addr = cpu_to_scr(tmp);
+       pm->sg.size = cpu_to_scr(-dp_ofs);
+
+out_ok:
+       sym_set_script_dp (np, cp, dp_scr);
+       OUTL_DSP (SCRIPTA_BA (np, clrack));
+       return;
+
+out_reject:
+       OUTL_DSP (SCRIPTB_BA (np, msg_bad));
+}
+
+
+/*
+ *  chip calculation of the data residual.
+ *
+ *  As I used to say, the requirement of data residual 
+ *  in SCSI is broken, useless and cannot be achieved 
+ *  without huge complexity.
+ *  But most OSes and even the official CAM require it.
+ *  When stupidity happens to be so widely spread inside 
+ *  a community, it gets hard to convince.
+ *
+ *  Anyway, I don't care, since I am not going to use 
+ *  any software that considers this data residual as 
+ *  a relevant information. :)
+ */
+
+int sym_compute_residual(hcb_p np, ccb_p cp)
+{
+       int dp_sg, dp_sgmin, resid = 0;
+       int dp_ofs = 0;
+
+       /*
+        *  Check for some data lost or just thrown away.
+        *  We are not required to be quite accurate in this 
+        *  situation. Btw, if we are odd for output and the 
+        *  device claims some more data, it may well happen 
+        *  than our residual be zero. :-)
+        */
+       if (cp->xerr_status & (XE_EXTRA_DATA|XE_SODL_UNRUN|XE_SWIDE_OVRUN)) {
+               if (cp->xerr_status & XE_EXTRA_DATA)
+                       resid -= cp->extra_bytes;
+               if (cp->xerr_status & XE_SODL_UNRUN)
+                       ++resid;
+               if (cp->xerr_status & XE_SWIDE_OVRUN)
+                       --resid;
+       }
+
+       /*
+        *  If all data has been transferred,
+        *  there is no residual.
+        */
+       if (cp->phys.head.lastp == sym_goalp(cp))
+               return resid;
+
+       /*
+        *  If no data transfer occurs, or if the data
+        *  pointer is weird, return full residual.
+        */
+       if (cp->startp == cp->phys.head.lastp ||
+           sym_evaluate_dp(np, cp, scr_to_cpu(cp->phys.head.lastp),
+                           &dp_ofs) < 0) {
+               return cp->data_len;
+       }
+
+       /*
+        *  If we were auto-sensing, then we are done.
+        */
+       if (cp->host_flags & HF_SENSE) {
+               return -dp_ofs;
+       }
+
+       /*
+        *  We are now full comfortable in the computation 
+        *  of the data residual (2's complement).
+        */
+       dp_sgmin = SYM_CONF_MAX_SG - cp->segments;
+       resid = -cp->ext_ofs;
+       for (dp_sg = cp->ext_sg; dp_sg < SYM_CONF_MAX_SG; ++dp_sg) {
+               u_int tmp = scr_to_cpu(cp->phys.data[dp_sg].size);
+               resid += (tmp & 0xffffff);
+       }
+
+       /*
+        *  Hopefully, the result is not too wrong.
+        */
+       return resid;
+}
+
+/*
+ *  Negotiation for WIDE and SYNCHRONOUS DATA TRANSFER.
+ *
+ *  When we try to negotiate, we append the negotiation message
+ *  to the identify and (maybe) simple tag message.
+ *  The host status field is set to HS_NEGOTIATE to mark this
+ *  situation.
+ *
+ *  If the target doesn't answer this message immediately
+ *  (as required by the standard), the SIR_NEGO_FAILED interrupt
+ *  will be raised eventually.
+ *  The handler removes the HS_NEGOTIATE status, and sets the
+ *  negotiated value to the default (async / nowide).
+ *
+ *  If we receive a matching answer immediately, we check it
+ *  for validity, and set the values.
+ *
+ *  If we receive a Reject message immediately, we assume the
+ *  negotiation has failed, and fall back to standard values.
+ *
+ *  If we receive a negotiation message while not in HS_NEGOTIATE
+ *  state, it's a target initiated negotiation. We prepare a
+ *  (hopefully) valid answer, set our parameters, and send back 
+ *  this answer to the target.
+ *
+ *  If the target doesn't fetch the answer (no message out phase),
+ *  we assume the negotiation has failed, and fall back to default
+ *  settings (SIR_NEGO_PROTO interrupt).
+ *
+ *  When we set the values, we adjust them in all ccbs belonging 
+ *  to this target, in the controller's register, and in the "phys"
+ *  field of the controller's struct sym_hcb.
+ */
+
+/*
+ *  chip handler for SYNCHRONOUS DATA TRANSFER REQUEST (SDTR) message.
+ */
+static int  
+sym_sync_nego_check(hcb_p np, int req, int target)
+{
+       tcb_p tp = &np->target[target];
+       u_char  chg, ofs, per, fak, div;
+
+       if (DEBUG_FLAGS & DEBUG_NEGO) {
+               sym_print_nego_msg(np, target, "sync msgin", np->msgin);
+       };
+
+       /*
+        *  Get requested values.
+        */
+       chg = 0;
+       per = np->msgin[3];
+       ofs = np->msgin[4];
+
+       /*
+        *  Check values against our limits.
+        */
+       if (ofs) {
+               if (ofs > np->maxoffs)
+                       {chg = 1; ofs = np->maxoffs;}
+               if (req) {
+                       if (ofs > tp->tinfo.user.offset)
+                               {chg = 1; ofs = tp->tinfo.user.offset;}
+               }
+       }
+
+       if (ofs) {
+               if (per < np->minsync)
+                       {chg = 1; per = np->minsync;}
+               if (req) {
+                       if (per < tp->tinfo.user.period)
+                               {chg = 1; per = tp->tinfo.user.period;}
+               }
+       }
+
+       /*
+        *  Get new chip synchronous parameters value.
+        */
+       div = fak = 0;
+       if (ofs && sym_getsync(np, 0, per, &div, &fak) < 0)
+               goto reject_it;
+
+       if (DEBUG_FLAGS & DEBUG_NEGO) {
+               PRINT_TARGET(np, target);
+               printf ("sdtr: ofs=%d per=%d div=%d fak=%d chg=%d.\n",
+                       ofs, per, div, fak, chg);
+       }
+
+       /*
+        *  If it was an answer we want to change, 
+        *  then it isn't acceptable. Reject it.
+        */
+       if (!req && chg)
+               goto reject_it;
+
+       /*
+        *  Apply new values.
+        */
+       sym_setsync (np, target, ofs, per, div, fak);
+
+       /*
+        *  It was an answer. We are done.
+        */
+       if (!req)
+               return 0;
+
+       /*
+        *  It was a request. Prepare an answer message.
+        */
+       np->msgout[0] = M_EXTENDED;
+       np->msgout[1] = 3;
+       np->msgout[2] = M_X_SYNC_REQ;
+       np->msgout[3] = per;
+       np->msgout[4] = ofs;
+
+       if (DEBUG_FLAGS & DEBUG_NEGO) {
+               sym_print_nego_msg(np, target, "sync msgout", np->msgout);
+       }
+
+       np->msgin [0] = M_NOOP;
+
+       return 0;
+
+reject_it:
+       sym_setsync (np, target, 0, 0, 0, 0);
+       return -1;
+}
+
+static void sym_sync_nego(hcb_p np, tcb_p tp, ccb_p cp)
+{
+       int req = 1;
+       int result;
+
+       /*
+        *  Request or answer ?
+        */
+       if (INB (HS_PRT) == HS_NEGOTIATE) {
+               OUTB (HS_PRT, HS_BUSY);
+               if (cp->nego_status && cp->nego_status != NS_SYNC)
+                       goto reject_it;
+               req = 0;
+       }
+
+       /*
+        *  Check and apply new values.
+        */
+       result = sym_sync_nego_check(np, req, cp->target);
+       if (result)     /* Not acceptable, reject it */
+               goto reject_it;
+       if (req) {      /* Was a request, send response. */
+               cp->nego_status = NS_SYNC;
+               OUTL_DSP (SCRIPTB_BA (np, sdtr_resp));
+       }
+       else            /* Was a response, we are done. */
+               OUTL_DSP (SCRIPTA_BA (np, clrack));
+       return;
+
+reject_it:
+       OUTL_DSP (SCRIPTB_BA (np, msg_bad));
+}
+
+/*
+ *  chip handler for PARALLEL PROTOCOL REQUEST (PPR) message.
+ */
+static int 
+sym_ppr_nego_check(hcb_p np, int req, int target)
+{
+       tcb_p tp = &np->target[target];
+       u_char  chg, ofs, per, fak, dt, div, wide;
+
+       if (DEBUG_FLAGS & DEBUG_NEGO) {
+               sym_print_nego_msg(np, target, "ppr msgin", np->msgin);
+       };
+
+       /*
+        *  Get requested values.
+        */
+       chg  = 0;
+       per  = np->msgin[3];
+       ofs  = np->msgin[5];
+       wide = np->msgin[6];
+       dt   = np->msgin[7] & PPR_OPT_DT;
+
+       /*
+        *  Check values against our limits.
+        */
+       if (wide > np->maxwide)
+               {chg = 1; wide = np->maxwide;}
+       if (!wide || !(np->features & FE_ULTRA3))
+               dt &= ~PPR_OPT_DT;
+       if (req) {
+               if (wide > tp->tinfo.user.width)
+                       {chg = 1; wide = tp->tinfo.user.width;}
+       }
+
+       if (!(np->features & FE_U3EN))  /* Broken U3EN bit not supported */
+               dt &= ~PPR_OPT_DT;
+
+       if (dt != (np->msgin[7] & PPR_OPT_MASK)) chg = 1;
+
+       if (ofs) {
+               if (dt) {
+                       if (ofs > np->maxoffs_dt)
+                               {chg = 1; ofs = np->maxoffs_dt;}
+               }
+               else if (ofs > np->maxoffs)
+                       {chg = 1; ofs = np->maxoffs;}
+               if (req) {
+                       if (ofs > tp->tinfo.user.offset)
+                               {chg = 1; ofs = tp->tinfo.user.offset;}
+               }
+       }
+
+       if (ofs) {
+               if (dt) {
+                       if (per < np->minsync_dt)
+                               {chg = 1; per = np->minsync_dt;}
+               }
+               else if (per < np->minsync)
+                       {chg = 1; per = np->minsync;}
+               if (req) {
+                       if (per < tp->tinfo.user.period)
+                               {chg = 1; per = tp->tinfo.user.period;}
+               }
+       }
+
+       /*
+        *  Get new chip synchronous parameters value.
+        */
+       div = fak = 0;
+       if (ofs && sym_getsync(np, dt, per, &div, &fak) < 0)
+               goto reject_it;
+
+       /*
+        *  If it was an answer we want to change, 
+        *  then it isn't acceptable. Reject it.
+        */
+       if (!req && chg)
+               goto reject_it;
+
+       /*
+        *  Apply new values.
+        */
+       sym_setpprot (np, target, dt, ofs, per, wide, div, fak);
+
+       /*
+        *  It was an answer. We are done.
+        */
+       if (!req)
+               return 0;
+
+       /*
+        *  It was a request. Prepare an answer message.
+        */
+       np->msgout[0] = M_EXTENDED;
+       np->msgout[1] = 6;
+       np->msgout[2] = M_X_PPR_REQ;
+       np->msgout[3] = per;
+       np->msgout[4] = 0;
+       np->msgout[5] = ofs;
+       np->msgout[6] = wide;
+       np->msgout[7] = dt;
+
+       if (DEBUG_FLAGS & DEBUG_NEGO) {
+               sym_print_nego_msg(np, target, "ppr msgout", np->msgout);
+       }
+
+       np->msgin [0] = M_NOOP;
+
+       return 0;
+
+reject_it:
+       sym_setpprot (np, target, 0, 0, 0, 0, 0, 0);
+       /*
+        *  If it is a device response that should result in  
+        *  ST, we may want to try a legacy negotiation later.
+        */
+       if (!req && !dt) {
+               tp->tinfo.goal.options = 0;
+               tp->tinfo.goal.width   = wide;
+               tp->tinfo.goal.period  = per;
+               tp->tinfo.goal.offset  = ofs;
+       }
+       return -1;
+}
+
+static void sym_ppr_nego(hcb_p np, tcb_p tp, ccb_p cp)
+{
+       int req = 1;
+       int result;
+
+       /*
+        *  Request or answer ?
+        */
+       if (INB (HS_PRT) == HS_NEGOTIATE) {
+               OUTB (HS_PRT, HS_BUSY);
+               if (cp->nego_status && cp->nego_status != NS_PPR)
+                       goto reject_it;
+               req = 0;
+       }
+
+       /*
+        *  Check and apply new values.
+        */
+       result = sym_ppr_nego_check(np, req, cp->target);
+       if (result)     /* Not acceptable, reject it */
+               goto reject_it;
+       if (req) {      /* Was a request, send response. */
+               cp->nego_status = NS_PPR;
+               OUTL_DSP (SCRIPTB_BA (np, ppr_resp));
+       }
+       else            /* Was a response, we are done. */
+               OUTL_DSP (SCRIPTA_BA (np, clrack));
+       return;
+
+reject_it:
+       OUTL_DSP (SCRIPTB_BA (np, msg_bad));
+}
+
+/*
+ *  chip handler for WIDE DATA TRANSFER REQUEST (WDTR) message.
+ */
+static int  
+sym_wide_nego_check(hcb_p np, int req, int target)
+{
+       tcb_p tp = &np->target[target];
+       u_char  chg, wide;
+
+       if (DEBUG_FLAGS & DEBUG_NEGO) {
+               sym_print_nego_msg(np, target, "wide msgin", np->msgin);
+       };
+
+       /*
+        *  Get requested values.
+        */
+       chg  = 0;
+       wide = np->msgin[3];
+
+       /*
+        *  Check values against our limits.
+        */
+       if (wide > np->maxwide)
+               {chg = 1; wide = np->maxwide;}
+       if (req) {
+               if (wide > tp->tinfo.user.width)
+                       {chg = 1; wide = tp->tinfo.user.width;}
+       }
+
+       if (DEBUG_FLAGS & DEBUG_NEGO) {
+               PRINT_TARGET(np, target);
+               printf ("wdtr: wide=%d chg=%d.\n", wide, chg);
+       }
+
+       /*
+        *  If it was an answer we want to change, 
+        *  then it isn't acceptable. Reject it.
+        */
+       if (!req && chg)
+               goto reject_it;
+
+       /*
+        *  Apply new values.
+        */
+       sym_setwide (np, target, wide);
+
+       /*
+        *  It was an answer. We are done.
+        */
+       if (!req)
+               return 0;
+
+       /*
+        *  It was a request. Prepare an answer message.
+        */
+       np->msgout[0] = M_EXTENDED;
+       np->msgout[1] = 2;
+       np->msgout[2] = M_X_WIDE_REQ;
+       np->msgout[3] = wide;
+
+       np->msgin [0] = M_NOOP;
+
+       if (DEBUG_FLAGS & DEBUG_NEGO) {
+               sym_print_nego_msg(np, target, "wide msgout", np->msgout);
+       }
+
+       return 0;
+
+reject_it:
+       return -1;
+}
+
+static void sym_wide_nego(hcb_p np, tcb_p tp, ccb_p cp)
+{
+       int req = 1;
+       int result;
+
+       /*
+        *  Request or answer ?
+        */
+       if (INB (HS_PRT) == HS_NEGOTIATE) {
+               OUTB (HS_PRT, HS_BUSY);
+               if (cp->nego_status && cp->nego_status != NS_WIDE)
+                       goto reject_it;
+               req = 0;
+       }
+
+       /*
+        *  Check and apply new values.
+        */
+       result = sym_wide_nego_check(np, req, cp->target);
+       if (result)     /* Not acceptable, reject it */
+               goto reject_it;
+       if (req) {      /* Was a request, send response. */
+               cp->nego_status = NS_WIDE;
+               OUTL_DSP (SCRIPTB_BA (np, wdtr_resp));
+       }
+       else {          /* Was a response. */
+               /*
+                * Negotiate for SYNC immediately after WIDE response.
+                * This allows to negotiate for both WIDE and SYNC on 
+                * a single SCSI command (Suggested by Justin Gibbs).
+                */
+               if (tp->tinfo.goal.offset) {
+                       np->msgout[0] = M_EXTENDED;
+                       np->msgout[1] = 3;
+                       np->msgout[2] = M_X_SYNC_REQ;
+                       np->msgout[3] = tp->tinfo.goal.period;
+                       np->msgout[4] = tp->tinfo.goal.offset;
+
+                       if (DEBUG_FLAGS & DEBUG_NEGO) {
+                               sym_print_nego_msg(np, cp->target,
+                                                  "sync msgout", np->msgout);
+                       }
+
+                       cp->nego_status = NS_SYNC;
+                       OUTB (HS_PRT, HS_NEGOTIATE);
+                       OUTL_DSP (SCRIPTB_BA (np, sdtr_resp));
+                       return;
+               }
+               else
+                       OUTL_DSP (SCRIPTA_BA (np, clrack));
+       };
+
+       return;
+
+reject_it:
+       OUTL_DSP (SCRIPTB_BA (np, msg_bad));
+}
+
+/*
+ *  Reset DT, SYNC or WIDE to default settings.
+ *
+ *  Called when a negotiation does not succeed either 
+ *  on rejection or on protocol error.
+ *
+ *  A target that understands a PPR message should never 
+ *  reject it, and messing with it is very unlikely.
+ *  So, if a PPR makes problems, we may just want to 
+ *  try a legacy negotiation later.
+ */
+static void sym_nego_default(hcb_p np, tcb_p tp, ccb_p cp)
+{
+       switch (cp->nego_status) {
+       case NS_PPR:
+#if 0
+               sym_setpprot (np, cp->target, 0, 0, 0, 0, 0, 0);
+#else
+               tp->tinfo.goal.options = 0;
+               if (tp->tinfo.goal.period < np->minsync)
+                       tp->tinfo.goal.period = np->minsync;
+               if (tp->tinfo.goal.offset > np->maxoffs)
+                       tp->tinfo.goal.offset = np->maxoffs;
+#endif
+               break;
+       case NS_SYNC:
+               sym_setsync (np, cp->target, 0, 0, 0, 0);
+               break;
+       case NS_WIDE:
+               sym_setwide (np, cp->target, 0);
+               break;
+       };
+       np->msgin [0] = M_NOOP;
+       np->msgout[0] = M_NOOP;
+       cp->nego_status = 0;
+}
+
+/*
+ *  chip handler for MESSAGE REJECT received in response to 
+ *  PPR, WIDE or SYNCHRONOUS negotiation.
+ */
+static void sym_nego_rejected(hcb_p np, tcb_p tp, ccb_p cp)
+{
+       sym_nego_default(np, tp, cp);
+       OUTB (HS_PRT, HS_BUSY);
+}
+
+/*
+ *  chip exception handler for programmed interrupts.
+ */
+static void sym_int_sir (hcb_p np)
+{
+       u_char  num     = INB (nc_dsps);
+       u32     dsa     = INL (nc_dsa);
+       ccb_p   cp      = sym_ccb_from_dsa(np, dsa);
+       u_char  target  = INB (nc_sdid) & 0x0f;
+       tcb_p   tp      = &np->target[target];
+       int     tmp;
+
+       if (DEBUG_FLAGS & DEBUG_TINY) printf ("I#%d", num);
+
+       switch (num) {
+#if   SYM_CONF_DMA_ADDRESSING_MODE == 2
+       /*
+        *  SCRIPTS tell us that we may have to update 
+        *  64 bit DMA segment registers.
+        */
+       case SIR_DMAP_DIRTY:
+               sym_update_dmap_regs(np);
+               goto out;
+#endif
+       /*
+        *  Command has been completed with error condition 
+        *  or has been auto-sensed.
+        */
+       case SIR_COMPLETE_ERROR:
+               sym_complete_error(np, cp);
+               return;
+       /*
+        *  The C code is currently trying to recover from something.
+        *  Typically, user want to abort some command.
+        */
+       case SIR_SCRIPT_STOPPED:
+       case SIR_TARGET_SELECTED:
+       case SIR_ABORT_SENT:
+               sym_sir_task_recovery(np, num);
+               return;
+       /*
+        *  The device didn't go to MSG OUT phase after having 
+        *  been selected with ATN. We donnot want to handle 
+        *  that.
+        */
+       case SIR_SEL_ATN_NO_MSG_OUT:
+               printf ("%s:%d: No MSG OUT phase after selection with ATN.\n",
+                       sym_name (np), target);
+               goto out_stuck;
+       /*
+        *  The device didn't switch to MSG IN phase after 
+        *  having reseleted the initiator.
+        */
+       case SIR_RESEL_NO_MSG_IN:
+               printf ("%s:%d: No MSG IN phase after reselection.\n",
+                       sym_name (np), target);
+               goto out_stuck;
+       /*
+        *  After reselection, the device sent a message that wasn't 
+        *  an IDENTIFY.
+        */
+       case SIR_RESEL_NO_IDENTIFY:
+               printf ("%s:%d: No IDENTIFY after reselection.\n",
+                       sym_name (np), target);
+               goto out_stuck;
+       /*
+        *  The device reselected a LUN we donnot know about.
+        */
+       case SIR_RESEL_BAD_LUN:
+               np->msgout[0] = M_RESET;
+               goto out;
+       /*
+        *  The device reselected for an untagged nexus and we 
+        *  haven't any.
+        */
+       case SIR_RESEL_BAD_I_T_L:
+               np->msgout[0] = M_ABORT;
+               goto out;
+       /*
+        *  The device reselected for a tagged nexus that we donnot 
+        *  have.
+        */
+       case SIR_RESEL_BAD_I_T_L_Q:
+               np->msgout[0] = M_ABORT_TAG;
+               goto out;
+       /*
+        *  The SCRIPTS let us know that the device has grabbed 
+        *  our message and will abort the job.
+        */
+       case SIR_RESEL_ABORTED:
+               np->lastmsg = np->msgout[0];
+               np->msgout[0] = M_NOOP;
+               printf ("%s:%d: message %x sent on bad reselection.\n",
+                       sym_name (np), target, np->lastmsg);
+               goto out;
+       /*
+        *  The SCRIPTS let us know that a message has been 
+        *  successfully sent to the device.
+        */
+       case SIR_MSG_OUT_DONE:
+               np->lastmsg = np->msgout[0];
+               np->msgout[0] = M_NOOP;
+               /* Should we really care of that */
+               if (np->lastmsg == M_PARITY || np->lastmsg == M_ID_ERROR) {
+                       if (cp) {
+                               cp->xerr_status &= ~XE_PARITY_ERR;
+                               if (!cp->xerr_status)
+                                       OUTOFFB (HF_PRT, HF_EXT_ERR);
+                       }
+               }
+               goto out;
+       /*
+        *  The device didn't send a GOOD SCSI status.
+        *  We may have some work to do prior to allow 
+        *  the SCRIPTS processor to continue.
+        */
+       case SIR_BAD_SCSI_STATUS:
+               if (!cp)
+                       goto out;
+               sym_sir_bad_scsi_status(np, num, cp);
+               return;
+       /*
+        *  We are asked by the SCRIPTS to prepare a 
+        *  REJECT message.
+        */
+       case SIR_REJECT_TO_SEND:
+               sym_print_msg(cp, "M_REJECT to send for ", np->msgin);
+               np->msgout[0] = M_REJECT;
+               goto out;
+       /*
+        *  We have been ODD at the end of a DATA IN 
+        *  transfer and the device didn't send a 
+        *  IGNORE WIDE RESIDUE message.
+        *  It is a data overrun condition.
+        */
+       case SIR_SWIDE_OVERRUN:
+               if (cp) {
+                       OUTONB (HF_PRT, HF_EXT_ERR);
+                       cp->xerr_status |= XE_SWIDE_OVRUN;
+               }
+               goto out;
+       /*
+        *  We have been ODD at the end of a DATA OUT 
+        *  transfer.
+        *  It is a data underrun condition.
+        */
+       case SIR_SODL_UNDERRUN:
+               if (cp) {
+                       OUTONB (HF_PRT, HF_EXT_ERR);
+                       cp->xerr_status |= XE_SODL_UNRUN;
+               }
+               goto out;
+       /*
+        *  The device wants us to tranfer more data than 
+        *  expected or in the wrong direction.
+        *  The number of extra bytes is in scratcha.
+        *  It is a data overrun condition.
+        */
+       case SIR_DATA_OVERRUN:
+               if (cp) {
+                       OUTONB (HF_PRT, HF_EXT_ERR);
+                       cp->xerr_status |= XE_EXTRA_DATA;
+                       cp->extra_bytes += INL (nc_scratcha);
+               }
+               goto out;
+       /*
+        *  The device switched to an illegal phase (4/5).
+        */
+       case SIR_BAD_PHASE:
+               if (cp) {
+                       OUTONB (HF_PRT, HF_EXT_ERR);
+                       cp->xerr_status |= XE_BAD_PHASE;
+               }
+               goto out;
+       /*
+        *  We received a message.
+        */
+       case SIR_MSG_RECEIVED:
+               if (!cp)
+                       goto out_stuck;
+               switch (np->msgin [0]) {
+               /*
+                *  We received an extended message.
+                *  We handle MODIFY DATA POINTER, SDTR, WDTR 
+                *  and reject all other extended messages.
+                */
+               case M_EXTENDED:
+                       switch (np->msgin [2]) {
+                       case M_X_MODIFY_DP:
+                               if (DEBUG_FLAGS & DEBUG_POINTER)
+                                       sym_print_msg(cp,"modify DP",np->msgin);
+                               tmp = (np->msgin[3]<<24) + (np->msgin[4]<<16) + 
+                                     (np->msgin[5]<<8)  + (np->msgin[6]);
+                               sym_modify_dp(np, tp, cp, tmp);
+                               return;
+                       case M_X_SYNC_REQ:
+                               sym_sync_nego(np, tp, cp);
+                               return;
+                       case M_X_PPR_REQ:
+                               sym_ppr_nego(np, tp, cp);
+                               return;
+                       case M_X_WIDE_REQ:
+                               sym_wide_nego(np, tp, cp);
+                               return;
+                       default:
+                               goto out_reject;
+                       }
+                       break;
+               /*
+                *  We received a 1/2 byte message not handled from SCRIPTS.
+                *  We are only expecting MESSAGE REJECT and IGNORE WIDE 
+                *  RESIDUE messages that haven't been anticipated by 
+                *  SCRIPTS on SWIDE full condition. Unanticipated IGNORE 
+                *  WIDE RESIDUE messages are aliased as MODIFY DP (-1).
+                */
+               case M_IGN_RESIDUE:
+                       if (DEBUG_FLAGS & DEBUG_POINTER)
+                               sym_print_msg(cp,"ign wide residue", np->msgin);
+                       if (cp->host_flags & HF_SENSE)
+                               OUTL_DSP (SCRIPTA_BA (np, clrack));
+                       else
+                               sym_modify_dp(np, tp, cp, -1);
+                       return;
+               case M_REJECT:
+                       if (INB (HS_PRT) == HS_NEGOTIATE)
+                               sym_nego_rejected(np, tp, cp);
+                       else {
+                               PRINT_ADDR(cp);
+                               printf ("M_REJECT received (%x:%x).\n",
+                                       scr_to_cpu(np->lastmsg), np->msgout[0]);
+                       }
+                       goto out_clrack;
+                       break;
+               default:
+                       goto out_reject;
+               }
+               break;
+       /*
+        *  We received an unknown message.
+        *  Ignore all MSG IN phases and reject it.
+        */
+       case SIR_MSG_WEIRD:
+               sym_print_msg(cp, "WEIRD message received", np->msgin);
+               OUTL_DSP (SCRIPTB_BA (np, msg_weird));
+               return;
+       /*
+        *  Negotiation failed.
+        *  Target does not send us the reply.
+        *  Remove the HS_NEGOTIATE status.
+        */
+       case SIR_NEGO_FAILED:
+               OUTB (HS_PRT, HS_BUSY);
+       /*
+        *  Negotiation failed.
+        *  Target does not want answer message.
+        */
+       case SIR_NEGO_PROTO:
+               sym_nego_default(np, tp, cp);
+               goto out;
+       };
+
+out:
+       OUTONB_STD ();
+       return;
+out_reject:
+       OUTL_DSP (SCRIPTB_BA (np, msg_bad));
+       return;
+out_clrack:
+       OUTL_DSP (SCRIPTA_BA (np, clrack));
+       return;
+out_stuck:
+       return;
+}
+
+/*
+ *  Acquire a control block
+ */
+ccb_p sym_get_ccb (hcb_p np, u_char tn, u_char ln, u_char tag_order)
+{
+       tcb_p tp = &np->target[tn];
+       lcb_p lp = sym_lp(np, tp, ln);
+       u_short tag = NO_TAG;
+       SYM_QUEHEAD *qp;
+       ccb_p cp = (ccb_p) 0;
+
+       /*
+        *  Look for a free CCB
+        */
+       if (sym_que_empty(&np->free_ccbq))
+               (void) sym_alloc_ccb(np);
+       qp = sym_remque_head(&np->free_ccbq);
+       if (!qp)
+               goto out;
+       cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+
+#ifndef SYM_OPT_HANDLE_DEVICE_QUEUEING
+       /*
+        *  If the LCB is not yet available and the LUN
+        *  has been probed ok, try to allocate the LCB.
+        */
+       if (!lp && sym_is_bit(tp->lun_map, ln)) {
+               lp = sym_alloc_lcb(np, tn, ln);
+               if (!lp)
+                       goto out_free;
+       }
+#endif
+
+       /*
+        *  If the LCB is not available here, then the 
+        *  logical unit is not yet discovered. For those 
+        *  ones only accept 1 SCSI IO per logical unit, 
+        *  since we cannot allow disconnections.
+        */
+       if (!lp) {
+               if (!sym_is_bit(tp->busy0_map, ln))
+                       sym_set_bit(tp->busy0_map, ln);
+               else
+                       goto out_free;
+       } else {
+               /*
+                *  If we have been asked for a tagged command.
+                */
+               if (tag_order) {
+                       /*
+                        *  Debugging purpose.
+                        */
+#ifndef SYM_OPT_HANDLE_DEVICE_QUEUEING
+                       assert(lp->busy_itl == 0);
+#endif
+                       /*
+                        *  Allocate resources for tags if not yet.
+                        */
+                       if (!lp->cb_tags) {
+                               sym_alloc_lcb_tags(np, tn, ln);
+                               if (!lp->cb_tags)
+                                       goto out_free;
+                       }
+                       /*
+                        *  Get a tag for this SCSI IO and set up
+                        *  the CCB bus address for reselection, 
+                        *  and count it for this LUN.
+                        *  Toggle reselect path to tagged.
+                        */
+                       if (lp->busy_itlq < SYM_CONF_MAX_TASK) {
+                               tag = lp->cb_tags[lp->ia_tag];
+                               if (++lp->ia_tag == SYM_CONF_MAX_TASK)
+                                       lp->ia_tag = 0;
+                               ++lp->busy_itlq;
+#ifndef SYM_OPT_HANDLE_DEVICE_QUEUEING
+                               lp->itlq_tbl[tag] = cpu_to_scr(cp->ccb_ba);
+                               lp->head.resel_sa =
+                                       cpu_to_scr(SCRIPTA_BA (np, resel_tag));
+#endif
+#ifdef SYM_OPT_LIMIT_COMMAND_REORDERING
+                               cp->tags_si = lp->tags_si;
+                               ++lp->tags_sum[cp->tags_si];
+                               ++lp->tags_since;
+#endif
+                       }
+                       else
+                               goto out_free;
+               }
+               /*
+                *  This command will not be tagged.
+                *  If we already have either a tagged or untagged 
+                *  one, refuse to overlap this untagged one.
+                */
+               else {
+                       /*
+                        *  Debugging purpose.
+                        */
+#ifndef SYM_OPT_HANDLE_DEVICE_QUEUEING
+                       assert(lp->busy_itl == 0 && lp->busy_itlq == 0);
+#endif
+                       /*
+                        *  Count this nexus for this LUN.
+                        *  Set up the CCB bus address for reselection.
+                        *  Toggle reselect path to untagged.
+                        */
+                       ++lp->busy_itl;
+#ifndef SYM_OPT_HANDLE_DEVICE_QUEUEING
+                       if (lp->busy_itl == 1) {
+                               lp->head.itl_task_sa = cpu_to_scr(cp->ccb_ba);
+                               lp->head.resel_sa =
+                                     cpu_to_scr(SCRIPTA_BA (np, resel_no_tag));
+                       }
+                       else
+                               goto out_free;
+#endif
+               }
+       }
+       /*
+        *  Put the CCB into the busy queue.
+        */
+       sym_insque_tail(&cp->link_ccbq, &np->busy_ccbq);
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+       if (lp) {
+               sym_remque(&cp->link2_ccbq);
+               sym_insque_tail(&cp->link2_ccbq, &lp->waiting_ccbq);
+       }
+
+#endif
+       /*
+        *  Remember all informations needed to free this CCB.
+        */
+       cp->to_abort = 0;
+       cp->tag    = tag;
+       cp->order  = tag_order;
+       cp->target = tn;
+       cp->lun    = ln;
+
+       if (DEBUG_FLAGS & DEBUG_TAGS) {
+               PRINT_LUN(np, tn, ln);
+               printf ("ccb @%p using tag %d.\n", cp, tag);
+       }
+
+out:
+       return cp;
+out_free:
+       sym_insque_head(&cp->link_ccbq, &np->free_ccbq);
+       return (ccb_p) 0;
+}
+
+/*
+ *  Release one control block
+ */
+void sym_free_ccb (hcb_p np, ccb_p cp)
+{
+       tcb_p tp = &np->target[cp->target];
+       lcb_p lp = sym_lp(np, tp, cp->lun);
+
+       if (DEBUG_FLAGS & DEBUG_TAGS) {
+               PRINT_LUN(np, cp->target, cp->lun);
+               printf ("ccb @%p freeing tag %d.\n", cp, cp->tag);
+       }
+
+       /*
+        *  If LCB available,
+        */
+       if (lp) {
+               /*
+                *  If tagged, release the tag, set the relect path 
+                */
+               if (cp->tag != NO_TAG) {
+#ifdef SYM_OPT_LIMIT_COMMAND_REORDERING
+                       --lp->tags_sum[cp->tags_si];
+#endif
+                       /*
+                        *  Free the tag value.
+                        */
+                       lp->cb_tags[lp->if_tag] = cp->tag;
+                       if (++lp->if_tag == SYM_CONF_MAX_TASK)
+                               lp->if_tag = 0;
+                       /*
+                        *  Make the reselect path invalid, 
+                        *  and uncount this CCB.
+                        */
+                       lp->itlq_tbl[cp->tag] = cpu_to_scr(np->bad_itlq_ba);
+                       --lp->busy_itlq;
+               } else {        /* Untagged */
+                       /*
+                        *  Make the reselect path invalid, 
+                        *  and uncount this CCB.
+                        */
+                       lp->head.itl_task_sa = cpu_to_scr(np->bad_itl_ba);
+                       --lp->busy_itl;
+               }
+               /*
+                *  If no JOB active, make the LUN reselect path invalid.
+                */
+               if (lp->busy_itlq == 0 && lp->busy_itl == 0)
+                       lp->head.resel_sa =
+                               cpu_to_scr(SCRIPTB_BA (np, resel_bad_lun));
+       }
+       /*
+        *  Otherwise, we only accept 1 IO per LUN.
+        *  Clear the bit that keeps track of this IO.
+        */
+       else
+               sym_clr_bit(tp->busy0_map, cp->lun);
+
+       /*
+        *  We donnot queue more than 1 ccb per target 
+        *  with negotiation at any time. If this ccb was 
+        *  used for negotiation, clear this info in the tcb.
+        */
+       if (cp == tp->nego_cp)
+               tp->nego_cp = 0;
+
+#ifdef SYM_CONF_IARB_SUPPORT
+       /*
+        *  If we just complete the last queued CCB,
+        *  clear this info that is no longer relevant.
+        */
+       if (cp == np->last_cp)
+               np->last_cp = 0;
+#endif
+
+       /*
+        *  Unmap user data from DMA map if needed.
+        */
+       sym_data_dmamap_unload(np, cp);
+
+       /*
+        *  Make this CCB available.
+        */
+       cp->cam_ccb = 0;
+       cp->host_status = HS_IDLE;
+       sym_remque(&cp->link_ccbq);
+       sym_insque_head(&cp->link_ccbq, &np->free_ccbq);
+
+#ifdef SYM_OPT_HANDLE_IO_TIMEOUT
+       /*
+        *  Cancel any pending timeout condition.
+        */
+       sym_untimeout_ccb(np, cp);
+#endif
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+       if (lp) {
+               sym_remque(&cp->link2_ccbq);
+               sym_insque_tail(&cp->link2_ccbq, &np->dummy_ccbq);
+               if (cp->started) {
+                       if (cp->tag != NO_TAG)
+                               --lp->started_tags;
+                       else
+                               --lp->started_no_tag;
+               }
+       }
+       cp->started = 0;
+#endif
+}
+
+/*
+ *  Allocate a CCB from memory and initialize its fixed part.
+ */
+static ccb_p sym_alloc_ccb(hcb_p np)
+{
+       ccb_p cp = 0;
+       int hcode;
+
+       /*
+        *  Prevent from allocating more CCBs than we can 
+        *  queue to the controller.
+        */
+       if (np->actccbs >= SYM_CONF_MAX_START)
+               return 0;
+
+       /*
+        *  Allocate memory for this CCB.
+        */
+       cp = sym_calloc_dma(sizeof(struct sym_ccb), "CCB");
+       if (!cp)
+               goto out_free;
+
+       /*
+        *  Allocate a bounce buffer for sense data.
+        */
+       cp->sns_bbuf = sym_calloc_dma(SYM_SNS_BBUF_LEN, "SNS_BBUF");
+       if (!cp->sns_bbuf)
+               goto out_free;
+
+       /*
+        *  Allocate a map for the DMA of user data.
+        */
+       if (sym_data_dmamap_create(np, cp))
+               goto out_free;
+
+       /*
+        *  Count it.
+        */
+       np->actccbs++;
+
+       /*
+        *  Compute the bus address of this ccb.
+        */
+       cp->ccb_ba = vtobus(cp);
+
+       /*
+        *  Insert this ccb into the hashed list.
+        */
+       hcode = CCB_HASH_CODE(cp->ccb_ba);
+       cp->link_ccbh = np->ccbh[hcode];
+       np->ccbh[hcode] = cp;
+
+       /*
+        *  Initialyze the start and restart actions.
+        */
+       cp->phys.head.go.start   = cpu_to_scr(SCRIPTA_BA (np, idle));
+       cp->phys.head.go.restart = cpu_to_scr(SCRIPTB_BA (np, bad_i_t_l));
+
+       /*
+        *  Initilialyze some other fields.
+        */
+       cp->phys.smsg_ext.addr = cpu_to_scr(HCB_BA(np, msgin[2]));
+
+       /*
+        *  Chain into free ccb queue.
+        */
+       sym_insque_head(&cp->link_ccbq, &np->free_ccbq);
+
+       /*
+        *  Chain into optionnal lists.
+        */
+#ifdef SYM_OPT_HANDLE_IO_TIMEOUT
+       sym_insque_head(&cp->tmo_linkq, &np->tmo0_ccbq);
+#endif
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+       sym_insque_head(&cp->link2_ccbq, &np->dummy_ccbq);
+#endif
+       return cp;
+out_free:
+       if (cp) {
+               if (cp->sns_bbuf)
+                       sym_mfree_dma(cp->sns_bbuf,SYM_SNS_BBUF_LEN,"SNS_BBUF");
+               sym_mfree_dma(cp, sizeof(*cp), "CCB");
+       }
+       return 0;
+}
+
+/*
+ *  Look up a CCB from a DSA value.
+ */
+static ccb_p sym_ccb_from_dsa(hcb_p np, u32 dsa)
+{
+       int hcode;
+       ccb_p cp;
+
+       hcode = CCB_HASH_CODE(dsa);
+       cp = np->ccbh[hcode];
+       while (cp) {
+               if (cp->ccb_ba == dsa)
+                       break;
+               cp = cp->link_ccbh;
+       }
+
+       return cp;
+}
+
+/*
+ *  Target control block initialisation.
+ *  Nothing important to do at the moment.
+ */
+static void sym_init_tcb (hcb_p np, u_char tn)
+{
+#if 0  /*  Hmmm... this checking looks paranoid. */
+       /*
+        *  Check some alignments required by the chip.
+        */     
+       assert (((offsetof(struct sym_reg, nc_sxfer) ^
+               offsetof(struct sym_tcb, head.sval)) &3) == 0);
+       assert (((offsetof(struct sym_reg, nc_scntl3) ^
+               offsetof(struct sym_tcb, head.wval)) &3) == 0);
+#endif
+}
+
+/*
+ *  Lun control block allocation and initialization.
+ */
+lcb_p sym_alloc_lcb (hcb_p np, u_char tn, u_char ln)
+{
+       tcb_p tp = &np->target[tn];
+       lcb_p lp = sym_lp(np, tp, ln);
+
+       /*
+        *  Already done, just return.
+        */
+       if (lp)
+               return lp;
+
+       /*
+        *  Donnot allow LUN control block 
+        *  allocation for not probed LUNs.
+        */
+       if (!sym_is_bit(tp->lun_map, ln))
+               return 0;
+
+       /*
+        *  Initialize the target control block if not yet.
+        */
+       sym_init_tcb (np, tn);
+
+       /*
+        *  Allocate the LCB bus address array.
+        *  Compute the bus address of this table.
+        */
+       if (ln && !tp->luntbl) {
+               int i;
+
+               tp->luntbl = sym_calloc_dma(256, "LUNTBL");
+               if (!tp->luntbl)
+                       goto fail;
+               for (i = 0 ; i < 64 ; i++)
+                       tp->luntbl[i] = cpu_to_scr(vtobus(&np->badlun_sa));
+               tp->head.luntbl_sa = cpu_to_scr(vtobus(tp->luntbl));
+       }
+
+       /*
+        *  Allocate the table of pointers for LUN(s) > 0, if needed.
+        */
+       if (ln && !tp->lunmp) {
+               tp->lunmp = sym_calloc(SYM_CONF_MAX_LUN * sizeof(lcb_p),
+                                  "LUNMP");
+               if (!tp->lunmp)
+                       goto fail;
+       }
+
+       /*
+        *  Allocate the lcb.
+        *  Make it available to the chip.
+        */
+       lp = sym_calloc_dma(sizeof(struct sym_lcb), "LCB");
+       if (!lp)
+               goto fail;
+       if (ln) {
+               tp->lunmp[ln] = lp;
+               tp->luntbl[ln] = cpu_to_scr(vtobus(lp));
+       }
+       else {
+               tp->lun0p = lp;
+               tp->head.lun0_sa = cpu_to_scr(vtobus(lp));
+       }
+
+       /*
+        *  Let the itl task point to error handling.
+        */
+       lp->head.itl_task_sa = cpu_to_scr(np->bad_itl_ba);
+
+       /*
+        *  Set the reselect pattern to our default. :)
+        */
+       lp->head.resel_sa = cpu_to_scr(SCRIPTB_BA (np, resel_bad_lun));
+
+       /*
+        *  Set user capabilities.
+        */
+       lp->user_flags = tp->usrflags & (SYM_DISC_ENABLED | SYM_TAGS_ENABLED);
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+       /*
+        *  Initialize device queueing.
+        */
+       sym_que_init(&lp->waiting_ccbq);
+       sym_que_init(&lp->started_ccbq);
+       lp->started_max   = SYM_CONF_MAX_TASK;
+       lp->started_limit = SYM_CONF_MAX_TASK;
+#endif
+       /*
+        *  If we are busy, count the IO.
+        */
+       if (sym_is_bit(tp->busy0_map, ln)) {
+               lp->busy_itl = 1;
+               sym_clr_bit(tp->busy0_map, ln);
+       }
+fail:
+       return lp;
+}
+
+/*
+ *  Allocate LCB resources for tagged command queuing.
+ */
+static void sym_alloc_lcb_tags (hcb_p np, u_char tn, u_char ln)
+{
+       tcb_p tp = &np->target[tn];
+       lcb_p lp = sym_lp(np, tp, ln);
+       int i;
+
+       /*
+        *  If LCB not available, try to allocate it.
+        */
+       if (!lp && !(lp = sym_alloc_lcb(np, tn, ln)))
+               goto fail;
+
+       /*
+        *  Allocate the task table and and the tag allocation 
+        *  circular buffer. We want both or none.
+        */
+       lp->itlq_tbl = sym_calloc_dma(SYM_CONF_MAX_TASK*4, "ITLQ_TBL");
+       if (!lp->itlq_tbl)
+               goto fail;
+       lp->cb_tags = sym_calloc(SYM_CONF_MAX_TASK, "CB_TAGS");
+       if (!lp->cb_tags) {
+               sym_mfree_dma(lp->itlq_tbl, SYM_CONF_MAX_TASK*4, "ITLQ_TBL");
+               lp->itlq_tbl = 0;
+               goto fail;
+       }
+
+       /*
+        *  Initialize the task table with invalid entries.
+        */
+       for (i = 0 ; i < SYM_CONF_MAX_TASK ; i++)
+               lp->itlq_tbl[i] = cpu_to_scr(np->notask_ba);
+
+       /*
+        *  Fill up the tag buffer with tag numbers.
+        */
+       for (i = 0 ; i < SYM_CONF_MAX_TASK ; i++)
+               lp->cb_tags[i] = i;
+
+       /*
+        *  Make the task table available to SCRIPTS, 
+        *  And accept tagged commands now.
+        */
+       lp->head.itlq_tbl_sa = cpu_to_scr(vtobus(lp->itlq_tbl));
+
+       return;
+fail:
+       return;
+}
+
+/*
+ *  Queue a SCSI IO to the controller.
+ */
+int sym_queue_scsiio(hcb_p np, cam_scsiio_p csio, ccb_p cp)
+{
+       tcb_p   tp;
+       lcb_p   lp;
+       u_char  idmsg, *msgptr;
+       u_int   msglen;
+
+       /*
+        *  Keep track of the IO in our CCB.
+        */
+       cp->cam_ccb = (cam_ccb_p) csio;
+
+       /*
+        *  Retreive the target descriptor.
+        */
+       tp = &np->target[cp->target];
+
+       /*
+        *  Retreive the lun descriptor.
+        */
+       lp = sym_lp(np, tp, cp->lun);
+
+       /*
+        *  Build the IDENTIFY message.
+        */
+       idmsg = M_IDENTIFY | cp->lun;
+       if (cp->tag != NO_TAG || (lp && (lp->curr_flags & SYM_DISC_ENABLED)))
+               idmsg |= 0x40;
+
+       msgptr = cp->scsi_smsg;
+       msglen = 0;
+       msgptr[msglen++] = idmsg;
+
+       /*
+        *  Build the tag message if present.
+        */
+       if (cp->tag != NO_TAG) {
+               u_char order = cp->order;
+
+               switch(order) {
+               case M_ORDERED_TAG:
+                       break;
+               case M_HEAD_TAG:
+                       break;
+               default:
+                       order = M_SIMPLE_TAG;
+               }
+#ifdef SYM_OPT_LIMIT_COMMAND_REORDERING
+               /*
+                *  Avoid too much reordering of SCSI commands.
+                *  The algorithm tries to prevent completion of any 
+                *  tagged command from being delayed against more 
+                *  than 3 times the max number of queued commands.
+                */
+               if (lp && lp->tags_since > 3*SYM_CONF_MAX_TAG) {
+                       lp->tags_si = !(lp->tags_si);
+                       if (lp->tags_sum[lp->tags_si]) {
+                               order = M_ORDERED_TAG;
+                               if ((DEBUG_FLAGS & DEBUG_TAGS)||sym_verbose>1) {
+                                       PRINT_ADDR(cp);
+                                       printf("ordered tag forced.\n");
+                               }
+                       }
+                       lp->tags_since = 0;
+               }
+#endif
+               msgptr[msglen++] = order;
+
+               /*
+                *  For less than 128 tags, actual tags are numbered 
+                *  1,3,5,..2*MAXTAGS+1,since we may have to deal 
+                *  with devices that have problems with #TAG 0 or too 
+                *  great #TAG numbers. For more tags (up to 256), 
+                *  we use directly our tag number.
+                */
+#if SYM_CONF_MAX_TASK > (512/4)
+               msgptr[msglen++] = cp->tag;
+#else
+               msgptr[msglen++] = (cp->tag << 1) + 1;
+#endif
+       }
+
+       /*
+        *  Build a negotiation message if needed.
+        *  (nego_status is filled by sym_prepare_nego())
+        */
+       cp->nego_status = 0;
+       if (tp->tinfo.curr.width   != tp->tinfo.goal.width  ||
+           tp->tinfo.curr.period  != tp->tinfo.goal.period ||
+           tp->tinfo.curr.offset  != tp->tinfo.goal.offset ||
+           tp->tinfo.curr.options != tp->tinfo.goal.options) {
+               if (!tp->nego_cp && lp)
+                       msglen += sym_prepare_nego(np, cp, 0, msgptr + msglen);
+       }
+
+       /*
+        *  Startqueue
+        */
+       cp->phys.head.go.start   = cpu_to_scr(SCRIPTA_BA (np, select));
+       cp->phys.head.go.restart = cpu_to_scr(SCRIPTA_BA (np, resel_dsa));
+
+       /*
+        *  select
+        */
+       cp->phys.select.sel_id          = cp->target;
+       cp->phys.select.sel_scntl3      = tp->head.wval;
+       cp->phys.select.sel_sxfer       = tp->head.sval;
+       cp->phys.select.sel_scntl4      = tp->head.uval;
+
+       /*
+        *  message
+        */
+       cp->phys.smsg.addr      = cpu_to_scr(CCB_BA (cp, scsi_smsg));
+       cp->phys.smsg.size      = cpu_to_scr(msglen);
+
+       /*
+        *  status
+        */
+       cp->host_xflags         = 0;
+       cp->host_status         = cp->nego_status ? HS_NEGOTIATE : HS_BUSY;
+       cp->ssss_status         = S_ILLEGAL;
+       cp->xerr_status         = 0;
+       cp->host_flags          = 0;
+       cp->extra_bytes         = 0;
+
+       /*
+        *  extreme data pointer.
+        *  shall be positive, so -1 is lower than lowest.:)
+        */
+       cp->ext_sg  = -1;
+       cp->ext_ofs = 0;
+
+       /*
+        *  Build the CDB and DATA descriptor block 
+        *  and start the IO.
+        */
+       return sym_setup_data_and_start(np, csio, cp);
+}
+
+/*
+ *  Reset a SCSI target (all LUNs of this target).
+ */
+int sym_reset_scsi_target(hcb_p np, int target)
+{
+       tcb_p tp;
+
+       if (target == np->myaddr || (u_int)target >= SYM_CONF_MAX_TARGET)
+               return -1;
+
+       tp = &np->target[target];
+       tp->to_reset = 1;
+
+       np->istat_sem = SEM;
+       OUTB (nc_istat, SIGP|SEM);
+
+       return 0;
+}
+
+/*
+ *  Abort a SCSI IO.
+ */
+int sym_abort_ccb(hcb_p np, ccb_p cp, int timed_out)
+{
+       /*
+        *  Check that the IO is active.
+        */
+       if (!cp || !cp->host_status || cp->host_status == HS_WAIT)
+               return -1;
+
+       /*
+        *  If a previous abort didn't succeed in time,
+        *  perform a BUS reset.
+        */
+       if (cp->to_abort) {
+               sym_reset_scsi_bus(np, 1);
+               return 0;
+       }
+
+       /*
+        *  Mark the CCB for abort and allow time for.
+        */
+       cp->to_abort = timed_out ? 2 : 1;
+
+       /*
+        *  Tell the SCRIPTS processor to stop and synchronize with us.
+        */
+       np->istat_sem = SEM;
+       OUTB (nc_istat, SIGP|SEM);
+       return 0;
+}
+
+int sym_abort_scsiio(hcb_p np, cam_ccb_p ccb, int timed_out)
+{
+       ccb_p cp;
+       SYM_QUEHEAD *qp;
+
+       /*
+        *  Look up our CCB control block.
+        */
+       cp = 0;
+       FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
+               ccb_p cp2 = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+               if (cp2->cam_ccb == ccb) {
+                       cp = cp2;
+                       break;
+               }
+       }
+
+       return sym_abort_ccb(np, cp, timed_out);
+}
+
+/*
+ *  Complete execution of a SCSI command with extented 
+ *  error, SCSI status error, or having been auto-sensed.
+ *
+ *  The SCRIPTS processor is not running there, so we 
+ *  can safely access IO registers and remove JOBs from  
+ *  the START queue.
+ *  SCRATCHA is assumed to have been loaded with STARTPOS 
+ *  before the SCRIPTS called the C code.
+ */
+void sym_complete_error (hcb_p np, ccb_p cp)
+{
+       tcb_p tp;
+       lcb_p lp;
+       int resid;
+       int i;
+
+       /*
+        *  Paranoid check. :)
+        */
+       if (!cp || !cp->cam_ccb)
+               return;
+
+       if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_RESULT)) {
+               printf ("CCB=%lx STAT=%x/%x/%x DEV=%d/%d\n", (unsigned long)cp,
+                       cp->host_status, cp->ssss_status, cp->host_flags,
+                       cp->target, cp->lun);
+               MDELAY(100);
+       }
+
+       /*
+        *  Get target and lun pointers.
+        */
+       tp = &np->target[cp->target];
+       lp = sym_lp(np, tp, cp->lun);
+
+       /*
+        *  Check for extended errors.
+        */
+       if (cp->xerr_status) {
+               if (sym_verbose)
+                       sym_print_xerr(cp, cp->xerr_status);
+               if (cp->host_status == HS_COMPLETE)
+                       cp->host_status = HS_COMP_ERR;
+       }
+
+       /*
+        *  Calculate the residual.
+        */
+       resid = sym_compute_residual(np, cp);
+
+       if (!SYM_SETUP_RESIDUAL_SUPPORT) {/* If user does not want residuals */
+               resid  = 0;              /* throw them away. :)             */
+               cp->sv_resid = 0;
+       }
+#ifdef DEBUG_2_0_X
+if (resid)
+       printf("XXXX RESID= %d - 0x%x\n", resid, resid);
+#endif
+
+       /*
+        *  Dequeue all queued CCBs for that device 
+        *  not yet started by SCRIPTS.
+        */
+       i = (INL (nc_scratcha) - np->squeue_ba) / 4;
+       i = sym_dequeue_from_squeue(np, i, cp->target, cp->lun, -1);
+
+       /*
+        *  Restart the SCRIPTS processor.
+        */
+       OUTL_DSP (SCRIPTA_BA (np, start));
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+       if (cp->host_status == HS_COMPLETE &&
+           cp->ssss_status == S_QUEUE_FULL) {
+               if (!lp || lp->started_tags - i < 2)
+                       goto weirdness;
+               /*
+                *  Decrease queue depth as needed.
+                */
+               lp->started_max = lp->started_tags - i - 1;
+               lp->num_sgood = 0;
+
+               if (sym_verbose >= 2) {
+                       PRINT_LUN(np, cp->target, cp->lun);
+                       printf(" queue depth is now %d\n", lp->started_max);
+               }
+
+               /*
+                *  Repair the CCB.
+                */
+               cp->host_status = HS_BUSY;
+               cp->ssss_status = S_ILLEGAL;
+
+               /*
+                *  Let's requeue it to device.
+                */
+               sym_set_cam_status(cp->cam_ccb, CAM_REQUEUE_REQ);
+               goto finish;
+       }
+weirdness:
+#endif
+       /*
+        *  Synchronize DMA map if needed.
+        */
+       sym_data_dmamap_postsync(np, cp);
+
+       /*
+        *  Build result in CAM ccb.
+        */
+       sym_set_cam_result_error(np, cp, resid);
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+finish:
+#endif
+       /*
+        *  Add this one to the COMP queue.
+        */
+       sym_remque(&cp->link_ccbq);
+       sym_insque_head(&cp->link_ccbq, &np->comp_ccbq);
+
+       /*
+        *  Complete all those commands with either error 
+        *  or requeue condition.
+        */
+       sym_flush_comp_queue(np, 0);
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+       /*
+        *  Donnot start more than 1 command after an error.
+        */
+       if (lp)
+               sym_start_next_ccbs(np, lp, 1);
+#endif
+}
+
+/*
+ *  Complete execution of a successful SCSI command.
+ *
+ *  Only successful commands go to the DONE queue, 
+ *  since we need to have the SCRIPTS processor 
+ *  stopped on any error condition.
+ *  The SCRIPTS processor is running while we are 
+ *  completing successful commands.
+ */
+void sym_complete_ok (hcb_p np, ccb_p cp)
+{
+       tcb_p tp;
+       lcb_p lp;
+       cam_ccb_p ccb;
+       int resid;
+
+       /*
+        *  Paranoid check. :)
+        */
+       if (!cp || !cp->cam_ccb)
+               return;
+       assert (cp->host_status == HS_COMPLETE);
+
+       /*
+        *  Get user command.
+        */
+       ccb = cp->cam_ccb;
+
+       /*
+        *  Get target and lun pointers.
+        */
+       tp = &np->target[cp->target];
+       lp = sym_lp(np, tp, cp->lun);
+
+       /*
+        *  Assume device discovered on first success.
+        */
+       if (!lp)
+               sym_set_bit(tp->lun_map, cp->lun);
+
+       /*
+        *  If all data have been transferred, given than no
+        *  extended error did occur, there is no residual.
+        */
+       resid = 0;
+       if (cp->phys.head.lastp != sym_goalp(cp))
+               resid = sym_compute_residual(np, cp);
+
+       /*
+        *  Wrong transfer residuals may be worse than just always 
+        *  returning zero. User can disable this feature from 
+        *  sym_conf.h. Residual support is enabled by default.
+        */
+       if (!SYM_SETUP_RESIDUAL_SUPPORT)
+               resid  = 0;
+#ifdef DEBUG_2_0_X
+if (resid)
+       printf("XXXX RESID= %d - 0x%x\n", resid, resid);
+#endif
+
+       /*
+        *  Synchronize DMA map if needed.
+        */
+       sym_data_dmamap_postsync(np, cp);
+
+       /*
+        *  Build result in CAM ccb.
+        */
+       sym_set_cam_result_ok(np, cp, resid);
+
+#ifdef SYM_OPT_SNIFF_INQUIRY
+       /*
+        *  On standard INQUIRY response (EVPD and CmDt 
+        *  not set), sniff out device capabilities.
+        */
+       if (cp->cdb_buf[0] == 0x12 && !(cp->cdb_buf[1] & 0x3))
+               sym_sniff_inquiry(np, cp->cam_ccb, resid);
+#endif
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+       /*
+        *  If max number of started ccbs had been reduced,
+        *  increase it if 200 good status received.
+        */
+       if (lp && lp->started_max < lp->started_limit) {
+               ++lp->num_sgood;
+               if (lp->num_sgood >= 200) {
+                       lp->num_sgood = 0;
+                       ++lp->started_max;
+                       if (sym_verbose >= 2) {
+                               PRINT_LUN(np, cp->target, cp->lun);
+                               printf(" queue depth is now %d\n",
+                                      lp->started_max);
+                       }
+               }
+       }
+#endif
+
+       /*
+        *  Free our CCB.
+        */
+       sym_free_ccb (np, cp);
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+       /*
+        *  Requeue a couple of awaiting scsi commands.
+        */
+       if (lp && !sym_que_empty(&lp->waiting_ccbq))
+               sym_start_next_ccbs(np, lp, 2);
+#endif
+       /*
+        *  Complete the command.
+        */
+       sym_xpt_done(np, ccb);
+}
+
+/*
+ *  Soft-attach the controller.
+ */
+#ifdef SYM_OPT_NVRAM_PRE_READ
+int sym_hcb_attach(hcb_p np, struct sym_fw *fw, struct sym_nvram *nvram)
+#else
+int sym_hcb_attach(hcb_p np, struct sym_fw *fw)
+#endif
+{
+#ifndef SYM_OPT_NVRAM_PRE_READ
+       struct sym_nvram nvram_buf, *nvram = &nvram_buf;
+#endif
+       int i;
+
+       /*
+        *  Get some info about the firmware.
+        */
+       np->scripta_sz   = fw->a_size;
+       np->scriptb_sz   = fw->b_size;
+       np->scriptz_sz   = fw->z_size;
+       np->fw_setup     = fw->setup;
+       np->fw_patch     = fw->patch;
+       np->fw_name      = fw->name;
+
+       /*
+        *  Save setting of some IO registers, so we will 
+        *  be able to probe specific implementations.
+        */
+       sym_save_initial_setting (np);
+
+       /*
+        *  Reset the chip now, since it has been reported 
+        *  that SCSI clock calibration may not work properly 
+        *  if the chip is currently active.
+        */
+       sym_chip_reset (np);
+
+       /*
+        *  Try to read the user set-up.
+        */
+#ifndef SYM_OPT_NVRAM_PRE_READ
+       (void) sym_read_nvram(np, nvram);
+#endif
+
+       /*
+        *  Prepare controller and devices settings, according 
+        *  to chip features, user set-up and driver set-up.
+        */
+       (void) sym_prepare_setting(np, nvram);
+
+       /*
+        *  Check the PCI clock frequency.
+        *  Must be performed after prepare_setting since it destroys 
+        *  STEST1 that is used to probe for the clock doubler.
+        */
+       i = sym_getpciclock(np);
+       if (i > 37000 && !(np->features & FE_66MHZ))
+               printf("%s: PCI BUS clock seems too high: %u KHz.\n",
+                       sym_name(np), i);
+
+       /*
+        *  Allocate the start queue.
+        */
+       np->squeue = (u32 *) sym_calloc_dma(sizeof(u32)*(MAX_QUEUE*2),"SQUEUE");
+       if (!np->squeue)
+               goto attach_failed;
+       np->squeue_ba = vtobus(np->squeue);
+
+       /*
+        *  Allocate the done queue.
+        */
+       np->dqueue = (u32 *) sym_calloc_dma(sizeof(u32)*(MAX_QUEUE*2),"DQUEUE");
+       if (!np->dqueue)
+               goto attach_failed;
+       np->dqueue_ba = vtobus(np->dqueue);
+
+       /*
+        *  Allocate the target bus address array.
+        */
+       np->targtbl = (u32 *) sym_calloc_dma(256, "TARGTBL");
+       if (!np->targtbl)
+               goto attach_failed;
+       np->targtbl_ba = vtobus(np->targtbl);
+
+       /*
+        *  Allocate SCRIPTS areas.
+        */
+       np->scripta0 = sym_calloc_dma(np->scripta_sz, "SCRIPTA0");
+       np->scriptb0 = sym_calloc_dma(np->scriptb_sz, "SCRIPTB0");
+       np->scriptz0 = sym_calloc_dma(np->scriptz_sz, "SCRIPTZ0");
+       if (!np->scripta0 || !np->scriptb0 || !np->scriptz0)
+               goto attach_failed;
+
+       /*
+        *  Allocate the array of lists of CCBs hashed by DSA.
+        */
+       np->ccbh = sym_calloc(sizeof(ccb_p *)*CCB_HASH_SIZE, "CCBH");
+       if (!np->ccbh)
+               goto attach_failed;
+
+       /*
+        *  Initialyze the CCB free and busy queues.
+        */
+       sym_que_init(&np->free_ccbq);
+       sym_que_init(&np->busy_ccbq);
+       sym_que_init(&np->comp_ccbq);
+
+       /*
+        *  Initializations for optional handling 
+        *  of IO timeouts and device queueing.
+        */
+#ifdef SYM_OPT_HANDLE_IO_TIMEOUT
+       sym_que_init(&np->tmo0_ccbq);
+       np->tmo_ccbq =
+               sym_calloc(2*SYM_CONF_TIMEOUT_ORDER_MAX*sizeof(SYM_QUEHEAD),
+                          "TMO_CCBQ");
+       for (i = 0 ; i < 2*SYM_CONF_TIMEOUT_ORDER_MAX ; i++)
+               sym_que_init(&np->tmo_ccbq[i]);
+#endif
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+       sym_que_init(&np->dummy_ccbq);
+#endif
+       /*
+        *  Allocate some CCB. We need at least ONE.
+        */
+       if (!sym_alloc_ccb(np))
+               goto attach_failed;
+
+       /*
+        *  Calculate BUS addresses where we are going 
+        *  to load the SCRIPTS.
+        */
+       np->scripta_ba  = vtobus(np->scripta0);
+       np->scriptb_ba  = vtobus(np->scriptb0);
+       np->scriptz_ba  = vtobus(np->scriptz0);
+
+       if (np->ram_ba) {
+               np->scripta_ba  = np->ram_ba;
+               if (np->features & FE_RAM8K) {
+                       np->ram_ws = 8192;
+                       np->scriptb_ba = np->scripta_ba + 4096;
+#if 0  /* May get useful for 64 BIT PCI addressing */
+                       np->scr_ram_seg = cpu_to_scr(np->scripta_ba >> 32);
+#endif
+               }
+               else
+                       np->ram_ws = 4096;
+       }
+
+       /*
+        *  Copy scripts to controller instance.
+        */
+       bcopy(fw->a_base, np->scripta0, np->scripta_sz);
+       bcopy(fw->b_base, np->scriptb0, np->scriptb_sz);
+       bcopy(fw->z_base, np->scriptz0, np->scriptz_sz);
+
+       /*
+        *  Setup variable parts in scripts and compute
+        *  scripts bus addresses used from the C code.
+        */
+       np->fw_setup(np, fw);
+
+       /*
+        *  Bind SCRIPTS with physical addresses usable by the 
+        *  SCRIPTS processor (as seen from the BUS = BUS addresses).
+        */
+       sym_fw_bind_script(np, (u32 *) np->scripta0, np->scripta_sz);
+       sym_fw_bind_script(np, (u32 *) np->scriptb0, np->scriptb_sz);
+       sym_fw_bind_script(np, (u32 *) np->scriptz0, np->scriptz_sz);
+
+#ifdef SYM_CONF_IARB_SUPPORT
+       /*
+        *    If user wants IARB to be set when we win arbitration 
+        *    and have other jobs, compute the max number of consecutive 
+        *    settings of IARB hints before we leave devices a chance to 
+        *    arbitrate for reselection.
+        */
+#ifdef SYM_SETUP_IARB_MAX
+       np->iarb_max = SYM_SETUP_IARB_MAX;
+#else
+       np->iarb_max = 4;
+#endif
+#endif
+
+       /*
+        *  Prepare the idle and invalid task actions.
+        */
+       np->idletask.start      = cpu_to_scr(SCRIPTA_BA (np, idle));
+       np->idletask.restart    = cpu_to_scr(SCRIPTB_BA (np, bad_i_t_l));
+       np->idletask_ba         = vtobus(&np->idletask);
+
+       np->notask.start        = cpu_to_scr(SCRIPTA_BA (np, idle));
+       np->notask.restart      = cpu_to_scr(SCRIPTB_BA (np, bad_i_t_l));
+       np->notask_ba           = vtobus(&np->notask);
+
+       np->bad_itl.start       = cpu_to_scr(SCRIPTA_BA (np, idle));
+       np->bad_itl.restart     = cpu_to_scr(SCRIPTB_BA (np, bad_i_t_l));
+       np->bad_itl_ba          = vtobus(&np->bad_itl);
+
+       np->bad_itlq.start      = cpu_to_scr(SCRIPTA_BA (np, idle));
+       np->bad_itlq.restart    = cpu_to_scr(SCRIPTB_BA (np,bad_i_t_l_q));
+       np->bad_itlq_ba         = vtobus(&np->bad_itlq);
+
+       /*
+        *  Allocate and prepare the lun JUMP table that is used 
+        *  for a target prior the probing of devices (bad lun table).
+        *  A private table will be allocated for the target on the 
+        *  first INQUIRY response received.
+        */
+       np->badluntbl = sym_calloc_dma(256, "BADLUNTBL");
+       if (!np->badluntbl)
+               goto attach_failed;
+
+       np->badlun_sa = cpu_to_scr(SCRIPTB_BA (np, resel_bad_lun));
+       for (i = 0 ; i < 64 ; i++)      /* 64 luns/target, no less */
+               np->badluntbl[i] = cpu_to_scr(vtobus(&np->badlun_sa));
+
+       /*
+        *  Prepare the bus address array that contains the bus 
+        *  address of each target control block.
+        *  For now, assume all logical units are wrong. :)
+        */
+       for (i = 0 ; i < SYM_CONF_MAX_TARGET ; i++) {
+               np->targtbl[i] = cpu_to_scr(vtobus(&np->target[i]));
+               np->target[i].head.luntbl_sa =
+                               cpu_to_scr(vtobus(np->badluntbl));
+               np->target[i].head.lun0_sa =
+                               cpu_to_scr(vtobus(&np->badlun_sa));
+       }
+
+       /*
+        *  Now check the cache handling of the pci chipset.
+        */
+       if (sym_snooptest (np)) {
+               printf("%s: CACHE INCORRECTLY CONFIGURED.\n", sym_name(np));
+               goto attach_failed;
+       };
+
+       /*
+        *  Sigh! we are done.
+        */
+       return 0;
+
+       /*
+        *  We have failed.
+        *  We will try to free all the resources we have 
+        *  allocated, but if we are a boot device, this 
+        *  will not help that much.;)
+        */
+attach_failed:
+               sym_hcb_free(np);
+       return -ENXIO;
+}
+
+/*
+ *  Free everything that has been allocated for this device.
+ */
+void sym_hcb_free(hcb_p np)
+{
+       SYM_QUEHEAD *qp;
+       ccb_p cp;
+       tcb_p tp;
+       lcb_p lp;
+       int target, lun;
+
+       if (np->scriptz0)
+               sym_mfree_dma(np->scriptz0, np->scriptz_sz, "SCRIPTZ0");
+       if (np->scriptb0)
+               sym_mfree_dma(np->scriptb0, np->scriptb_sz, "SCRIPTB0");
+       if (np->scripta0)
+               sym_mfree_dma(np->scripta0, np->scripta_sz, "SCRIPTA0");
+#ifdef SYM_OPT_HANDLE_IO_TIMEOUT
+       if (np->tmo_ccbq)
+               sym_mfree(np->tmo_ccbq,
+                         2*SYM_CONF_TIMEOUT_ORDER_MAX*sizeof(SYM_QUEHEAD),
+                         "TMO_CCBQ");
+#endif
+       if (np->squeue)
+               sym_mfree_dma(np->squeue, sizeof(u32)*(MAX_QUEUE*2), "SQUEUE");
+       if (np->dqueue)
+               sym_mfree_dma(np->dqueue, sizeof(u32)*(MAX_QUEUE*2), "DQUEUE");
+
+       if (np->actccbs) {
+               while ((qp = sym_remque_head(&np->free_ccbq)) != 0) {
+                       cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
+                       sym_data_dmamap_destroy(np, cp);
+                       sym_mfree_dma(cp->sns_bbuf, SYM_SNS_BBUF_LEN,
+                                     "SNS_BBUF");
+                       sym_mfree_dma(cp, sizeof(*cp), "CCB");
+               }
+       }
+       if (np->ccbh)
+               sym_mfree(np->ccbh, sizeof(ccb_p *)*CCB_HASH_SIZE, "CCBH");
+
+       if (np->badluntbl)
+               sym_mfree_dma(np->badluntbl, 256,"BADLUNTBL");
+
+       for (target = 0; target < SYM_CONF_MAX_TARGET ; target++) {
+               tp = &np->target[target];
+               for (lun = 0 ; lun < SYM_CONF_MAX_LUN ; lun++) {
+                       lp = sym_lp(np, tp, lun);
+                       if (!lp)
+                               continue;
+                       if (lp->itlq_tbl)
+                               sym_mfree_dma(lp->itlq_tbl, SYM_CONF_MAX_TASK*4,
+                                      "ITLQ_TBL");
+                       if (lp->cb_tags)
+                               sym_mfree(lp->cb_tags, SYM_CONF_MAX_TASK,
+                                      "CB_TAGS");
+                       sym_mfree_dma(lp, sizeof(*lp), "LCB");
+               }
+#if SYM_CONF_MAX_LUN > 1
+               if (tp->lunmp)
+                       sym_mfree(tp->lunmp, SYM_CONF_MAX_LUN*sizeof(lcb_p),
+                              "LUNMP");
+#endif 
+       }
+       if (np->targtbl)
+               sym_mfree_dma(np->targtbl, 256, "TARGTBL");
+}
diff --git a/xen/drivers/scsi/sym53c8xx_2/sym_hipd.h b/xen/drivers/scsi/sym53c8xx_2/sym_hipd.h
new file mode 100644 (file)
index 0000000..cd8d791
--- /dev/null
@@ -0,0 +1,1446 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000  Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ *         Wolfgang Stanglmeier        <wolf@cologne.de>
+ *         Stefan Esser                <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994  Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef SYM_HIPD_H
+#define SYM_HIPD_H
+
+/*
+ *  Generic driver options.
+ *
+ *  They may be defined in platform specific headers, if they 
+ *  are useful.
+ *
+ *    SYM_OPT_NO_BUS_MEMORY_MAPPING
+ *        When this option is set, the driver will not load the 
+ *        on-chip RAM using MMIO, but let the SCRIPTS processor 
+ *        do the work using MOVE MEMORY instructions.
+ *        (set for Linux/PPC)
+ *
+ *    SYM_OPT_HANDLE_DIR_UNKNOWN
+ *        When this option is set, the SCRIPTS used by the driver 
+ *        are able to handle SCSI transfers with direction not 
+ *        supplied by user.
+ *        (set for Linux-2.0.X)
+ *
+ *    SYM_OPT_HANDLE_DEVICE_QUEUEING
+ *        When this option is set, the driver will use a queue per 
+ *        device and handle QUEUE FULL status requeuing internally.
+ *
+ *    SYM_OPT_BUS_DMA_ABSTRACTION
+ *        When this option is set, the driver allocator is responsible 
+ *        of maintaining bus physical addresses and so provides virtual 
+ *        to bus physical address translation of driver data structures.
+ *        (set for FreeBSD-4 and Linux 2.3)
+ *
+ *    SYM_OPT_SNIFF_INQUIRY
+ *        When this option is set, the driver sniff out successful 
+ *        INQUIRY response and performs negotiations accordingly.
+ *        (set for Linux)
+ *
+ *    SYM_OPT_LIMIT_COMMAND_REORDERING
+ *        When this option is set, the driver tries to limit tagged 
+ *        command reordering to some reasonnable value.
+ *        (set for Linux)
+ */
+#if 0
+#define SYM_OPT_NO_BUS_MEMORY_MAPPING
+#define SYM_OPT_HANDLE_DIR_UNKNOWN
+#define SYM_OPT_HANDLE_DEVICE_QUEUEING
+#define SYM_OPT_BUS_DMA_ABSTRACTION
+#define SYM_OPT_SNIFF_INQUIRY
+#define SYM_OPT_LIMIT_COMMAND_REORDERING
+#endif
+
+/*
+ *  Active debugging tags and verbosity.
+ *  Both DEBUG_FLAGS and sym_verbose can be redefined 
+ *  by the platform specific code to something else.
+ */
+#define DEBUG_ALLOC    (0x0001)
+#define DEBUG_PHASE    (0x0002)
+#define DEBUG_POLL     (0x0004)
+#define DEBUG_QUEUE    (0x0008)
+#define DEBUG_RESULT   (0x0010)
+#define DEBUG_SCATTER  (0x0020)
+#define DEBUG_SCRIPT   (0x0040)
+#define DEBUG_TINY     (0x0080)
+#define DEBUG_TIMING   (0x0100)
+#define DEBUG_NEGO     (0x0200)
+#define DEBUG_TAGS     (0x0400)
+#define DEBUG_POINTER  (0x0800)
+
+#ifndef DEBUG_FLAGS
+#define DEBUG_FLAGS    (0x0000)
+#endif
+
+#ifndef sym_verbose
+#define sym_verbose    (np->verbose)
+#endif
+
+/*
+ *  These ones should have been already defined.
+ */
+#ifndef offsetof
+#define offsetof(t, m) ((size_t) (&((t *)0)->m))
+#endif
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef assert
+#define        assert(expression) { \
+       if (!(expression)) { \
+               (void)panic( \
+                       "assertion \"%s\" failed: file \"%s\", line %d\n", \
+                       #expression, \
+                       __FILE__, __LINE__); \
+       } \
+}
+#endif
+
+/*
+ *  Number of tasks per device we want to handle.
+ */
+#if    SYM_CONF_MAX_TAG_ORDER > 8
+#error "more than 256 tags per logical unit not allowed."
+#endif
+#define        SYM_CONF_MAX_TASK       (1<<SYM_CONF_MAX_TAG_ORDER)
+
+/*
+ *  Donnot use more tasks that we can handle.
+ */
+#ifndef        SYM_CONF_MAX_TAG
+#define        SYM_CONF_MAX_TAG        SYM_CONF_MAX_TASK
+#endif
+#if    SYM_CONF_MAX_TAG > SYM_CONF_MAX_TASK
+#undef SYM_CONF_MAX_TAG
+#define        SYM_CONF_MAX_TAG        SYM_CONF_MAX_TASK
+#endif
+
+/*
+ *    This one means 'NO TAG for this job'
+ */
+#define NO_TAG (256)
+
+/*
+ *  Number of SCSI targets.
+ */
+#if    SYM_CONF_MAX_TARGET > 16
+#error "more than 16 targets not allowed."
+#endif
+
+/*
+ *  Number of logical units per target.
+ */
+#if    SYM_CONF_MAX_LUN > 64
+#error "more than 64 logical units per target not allowed."
+#endif
+
+/*
+ *    Asynchronous pre-scaler (ns). Shall be 40 for 
+ *    the SCSI timings to be compliant.
+ */
+#define        SYM_CONF_MIN_ASYNC (40)
+
+/*
+ *  Number of entries in the START and DONE queues.
+ *
+ *  We limit to 1 PAGE in order to succeed allocation of 
+ *  these queues. Each entry is 8 bytes long (2 DWORDS).
+ */
+#ifdef SYM_CONF_MAX_START
+#define        SYM_CONF_MAX_QUEUE (SYM_CONF_MAX_START+2)
+#else
+#define        SYM_CONF_MAX_QUEUE (7*SYM_CONF_MAX_TASK+2)
+#define        SYM_CONF_MAX_START (SYM_CONF_MAX_QUEUE-2)
+#endif
+
+#if    SYM_CONF_MAX_QUEUE > SYM_MEM_CLUSTER_SIZE/8
+#undef SYM_CONF_MAX_QUEUE
+#define        SYM_CONF_MAX_QUEUE (SYM_MEM_CLUSTER_SIZE/8)
+#undef SYM_CONF_MAX_START
+#define        SYM_CONF_MAX_START (SYM_CONF_MAX_QUEUE-2)
+#endif
+
+/*
+ *  For this one, we want a short name :-)
+ */
+#define MAX_QUEUE      SYM_CONF_MAX_QUEUE
+
+/*
+ *  Union of supported NVRAM formats.
+ */
+struct sym_nvram {
+       int type;
+#define        SYM_SYMBIOS_NVRAM       (1)
+#define        SYM_TEKRAM_NVRAM        (2)
+#if SYM_CONF_NVRAM_SUPPORT
+       union {
+               Symbios_nvram Symbios;
+               Tekram_nvram Tekram;
+       } data;
+#endif
+};
+
+/*
+ *  Common definitions for both bus space based and legacy IO methods.
+ */
+#define INB(r)         INB_OFF(offsetof(struct sym_reg,r))
+#define INW(r)         INW_OFF(offsetof(struct sym_reg,r))
+#define INL(r)         INL_OFF(offsetof(struct sym_reg,r))
+
+#define OUTB(r, v)     OUTB_OFF(offsetof(struct sym_reg,r), (v))
+#define OUTW(r, v)     OUTW_OFF(offsetof(struct sym_reg,r), (v))
+#define OUTL(r, v)     OUTL_OFF(offsetof(struct sym_reg,r), (v))
+
+#define OUTONB(r, m)   OUTB(r, INB(r) | (m))
+#define OUTOFFB(r, m)  OUTB(r, INB(r) & ~(m))
+#define OUTONW(r, m)   OUTW(r, INW(r) | (m))
+#define OUTOFFW(r, m)  OUTW(r, INW(r) & ~(m))
+#define OUTONL(r, m)   OUTL(r, INL(r) | (m))
+#define OUTOFFL(r, m)  OUTL(r, INL(r) & ~(m))
+
+/*
+ *  We normally want the chip to have a consistent view
+ *  of driver internal data structures when we restart it.
+ *  Thus these macros.
+ */
+#define OUTL_DSP(v)                            \
+       do {                                    \
+               MEMORY_WRITE_BARRIER();         \
+               OUTL (nc_dsp, (v));             \
+       } while (0)
+
+#define OUTONB_STD()                           \
+       do {                                    \
+               MEMORY_WRITE_BARRIER();         \
+               OUTONB (nc_dcntl, (STD|NOCOM)); \
+       } while (0)
+
+/*
+ *  Command control block states.
+ */
+#define HS_IDLE                (0)
+#define HS_BUSY                (1)
+#define HS_NEGOTIATE   (2)     /* sync/wide data transfer*/
+#define HS_DISCONNECT  (3)     /* Disconnected by target */
+#define HS_WAIT                (4)     /* waiting for resource   */
+
+#define HS_DONEMASK    (0x80)
+#define HS_COMPLETE    (4|HS_DONEMASK)
+#define HS_SEL_TIMEOUT (5|HS_DONEMASK) /* Selection timeout      */
+#define HS_UNEXPECTED  (6|HS_DONEMASK) /* Unexpected disconnect  */
+#define HS_COMP_ERR    (7|HS_DONEMASK) /* Completed with error   */
+
+/*
+ *  Software Interrupt Codes
+ */
+#define        SIR_BAD_SCSI_STATUS     (1)
+#define        SIR_SEL_ATN_NO_MSG_OUT  (2)
+#define        SIR_MSG_RECEIVED        (3)
+#define        SIR_MSG_WEIRD           (4)
+#define        SIR_NEGO_FAILED         (5)
+#define        SIR_NEGO_PROTO          (6)
+#define        SIR_SCRIPT_STOPPED      (7)
+#define        SIR_REJECT_TO_SEND      (8)
+#define        SIR_SWIDE_OVERRUN       (9)
+#define        SIR_SODL_UNDERRUN       (10)
+#define        SIR_RESEL_NO_MSG_IN     (11)
+#define        SIR_RESEL_NO_IDENTIFY   (12)
+#define        SIR_RESEL_BAD_LUN       (13)
+#define        SIR_TARGET_SELECTED     (14)
+#define        SIR_RESEL_BAD_I_T_L     (15)
+#define        SIR_RESEL_BAD_I_T_L_Q   (16)
+#define        SIR_ABORT_SENT          (17)
+#define        SIR_RESEL_ABORTED       (18)
+#define        SIR_MSG_OUT_DONE        (19)
+#define        SIR_COMPLETE_ERROR      (20)
+#define        SIR_DATA_OVERRUN        (21)
+#define        SIR_BAD_PHASE           (22)
+#if    SYM_CONF_DMA_ADDRESSING_MODE == 2
+#define        SIR_DMAP_DIRTY          (23)
+#define        SIR_MAX                 (23)
+#else
+#define        SIR_MAX                 (22)
+#endif
+
+/*
+ *  Extended error bit codes.
+ *  xerr_status field of struct sym_ccb.
+ */
+#define        XE_EXTRA_DATA   (1)     /* unexpected data phase         */
+#define        XE_BAD_PHASE    (1<<1)  /* illegal phase (4/5)           */
+#define        XE_PARITY_ERR   (1<<2)  /* unrecovered SCSI parity error */
+#define        XE_SODL_UNRUN   (1<<3)  /* ODD transfer in DATA OUT phase */
+#define        XE_SWIDE_OVRUN  (1<<4)  /* ODD transfer in DATA IN phase */
+
+/*
+ *  Negotiation status.
+ *  nego_status field of struct sym_ccb.
+ */
+#define NS_SYNC                (1)
+#define NS_WIDE                (2)
+#define NS_PPR         (3)
+
+/*
+ *  A CCB hashed table is used to retrieve CCB address 
+ *  from DSA value.
+ */
+#define CCB_HASH_SHIFT         8
+#define CCB_HASH_SIZE          (1UL << CCB_HASH_SHIFT)
+#define CCB_HASH_MASK          (CCB_HASH_SIZE-1)
+#if 1
+#define CCB_HASH_CODE(dsa)     \
+       (((dsa) >> (_LGRU16_(sizeof(struct sym_ccb)))) & CCB_HASH_MASK)
+#else
+#define CCB_HASH_CODE(dsa)     (((dsa) >> 9) & CCB_HASH_MASK)
+#endif
+
+#if    SYM_CONF_DMA_ADDRESSING_MODE == 2
+/*
+ *  We may want to use segment registers for 64 bit DMA.
+ *  16 segments registers -> up to 64 GB addressable.
+ */
+#define SYM_DMAP_SHIFT (4)
+#define SYM_DMAP_SIZE  (1u<<SYM_DMAP_SHIFT)
+#define SYM_DMAP_MASK  (SYM_DMAP_SIZE-1)
+#endif
+
+/*
+ *  Device flags.
+ */
+#define SYM_DISC_ENABLED       (1)
+#define SYM_TAGS_ENABLED       (1<<1)
+#define SYM_SCAN_BOOT_DISABLED (1<<2)
+#define SYM_SCAN_LUNS_DISABLED (1<<3)
+
+/*
+ *  Host adapter miscellaneous flags.
+ */
+#define SYM_AVOID_BUS_RESET    (1)
+#define SYM_SCAN_TARGETS_HILO  (1<<1)
+
+/*
+ *  Misc.
+ */
+#define SYM_SNOOP_TIMEOUT (10000000)
+#define BUS_8_BIT      0
+#define BUS_16_BIT     1
+
+/*
+ *  Gather negotiable parameters value
+ */
+struct sym_trans {
+       u8 scsi_version;
+       u8 spi_version;
+       u8 period;
+       u8 offset;
+       u8 width;
+       u8 options;     /* PPR options */
+};
+
+struct sym_tinfo {
+       struct sym_trans curr;
+       struct sym_trans goal;
+       struct sym_trans user;
+#ifdef SYM_OPT_ANNOUNCE_TRANSFER_RATE
+       struct sym_trans prev;
+#endif
+};
+
+/*
+ *  Global TCB HEADER.
+ *
+ *  Due to lack of indirect addressing on earlier NCR chips,
+ *  this substructure is copied from the TCB to a global 
+ *  address after selection.
+ *  For SYMBIOS chips that support LOAD/STORE this copy is 
+ *  not needed and thus not performed.
+ */
+struct sym_tcbh {
+       /*
+        *  Scripts bus addresses of LUN table accessed from scripts.
+        *  LUN #0 is a special case, since multi-lun devices are rare, 
+        *  and we we want to speed-up the general case and not waste 
+        *  resources.
+        */
+       u32     luntbl_sa;      /* bus address of this table    */
+       u32     lun0_sa;        /* bus address of LCB #0        */
+       /*
+        *  Actual SYNC/WIDE IO registers value for this target.
+        *  'sval', 'wval' and 'uval' are read from SCRIPTS and 
+        *  so have alignment constraints.
+        */
+/*0*/  u_char  uval;           /* -> SCNTL4 register           */
+/*1*/  u_char  sval;           /* -> SXFER  io register        */
+/*2*/  u_char  filler1;
+/*3*/  u_char  wval;           /* -> SCNTL3 io register        */
+};
+
+/*
+ *  Target Control Block
+ */
+struct sym_tcb {
+       /*
+        *  TCB header.
+        *  Assumed at offset 0.
+        */
+/*0*/  struct sym_tcbh head;
+
+       /*
+        *  LUN table used by the SCRIPTS processor.
+        *  An array of bus addresses is used on reselection.
+        */
+       u32     *luntbl;        /* LCBs bus address table       */
+
+       /*
+        *  LUN table used by the C code.
+        */
+       lcb_p   lun0p;          /* LCB of LUN #0 (usual case)   */
+#if SYM_CONF_MAX_LUN > 1
+       lcb_p   *lunmp;         /* Other LCBs [1..MAX_LUN]      */
+#endif
+
+       /*
+        *  Bitmap that tells about LUNs that succeeded at least 
+        *  1 IO and therefore assumed to be a real device.
+        *  Avoid useless allocation of the LCB structure.
+        */
+       u32     lun_map[(SYM_CONF_MAX_LUN+31)/32];
+
+       /*
+        *  Bitmap that tells about LUNs that haven't yet an LCB 
+        *  allocated (not discovered or LCB allocation failed).
+        */
+       u32     busy0_map[(SYM_CONF_MAX_LUN+31)/32];
+
+#ifdef SYM_HAVE_STCB
+       /*
+        *  O/S specific data structure.
+        */
+       struct sym_stcb s;
+#endif
+
+       /*
+        *  Transfer capabilities (SIP)
+        */
+       struct sym_tinfo tinfo;
+
+       /*
+        * Keep track of the CCB used for the negotiation in order
+        * to ensure that only 1 negotiation is queued at a time.
+        */
+       ccb_p   nego_cp;        /* CCB used for the nego                */
+
+       /*
+        *  Set when we want to reset the device.
+        */
+       u_char  to_reset;
+
+       /*
+        *  Other user settable limits and options.
+        *  These limits are read from the NVRAM if present.
+        */
+       u_char  usrflags;
+       u_short usrtags;
+
+#ifdef SYM_OPT_SNIFF_INQUIRY
+       /*
+        *  Some minimal information from INQUIRY response.
+        */
+       u32     cmdq_map[(SYM_CONF_MAX_LUN+31)/32];
+       u_char  inq_version;
+       u_char  inq_byte7;
+       u_char  inq_byte56;
+       u_char  inq_byte7_valid;
+#endif
+
+};
+
+/*
+ *  Global LCB HEADER.
+ *
+ *  Due to lack of indirect addressing on earlier NCR chips,
+ *  this substructure is copied from the LCB to a global 
+ *  address after selection.
+ *  For SYMBIOS chips that support LOAD/STORE this copy is 
+ *  not needed and thus not performed.
+ */
+struct sym_lcbh {
+       /*
+        *  SCRIPTS address jumped by SCRIPTS on reselection.
+        *  For not probed logical units, this address points to 
+        *  SCRIPTS that deal with bad LU handling (must be at 
+        *  offset zero of the LCB for that reason).
+        */
+/*0*/  u32     resel_sa;
+
+       /*
+        *  Task (bus address of a CCB) read from SCRIPTS that points 
+        *  to the unique ITL nexus allowed to be disconnected.
+        */
+       u32     itl_task_sa;
+
+       /*
+        *  Task table bus address (read from SCRIPTS).
+        */
+       u32     itlq_tbl_sa;
+};
+
+/*
+ *  Logical Unit Control Block
+ */
+struct sym_lcb {
+       /*
+        *  TCB header.
+        *  Assumed at offset 0.
+        */
+/*0*/  struct sym_lcbh head;
+
+       /*
+        *  Task table read from SCRIPTS that contains pointers to 
+        *  ITLQ nexuses. The bus address read from SCRIPTS is 
+        *  inside the header.
+        */
+       u32     *itlq_tbl;      /* Kernel virtual address       */
+
+       /*
+        *  Busy CCBs management.
+        */
+       u_short busy_itlq;      /* Number of busy tagged CCBs   */
+       u_short busy_itl;       /* Number of busy untagged CCBs */
+
+       /*
+        *  Circular tag allocation buffer.
+        */
+       u_short ia_tag;         /* Tag allocation index         */
+       u_short if_tag;         /* Tag release index            */
+       u_char  *cb_tags;       /* Circular tags buffer         */
+
+       /*
+        *  O/S specific data structure.
+        */
+#ifdef SYM_HAVE_SLCB
+       struct sym_slcb s;
+#endif
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+       /*
+        *  Optionnaly the driver can handle device queueing, 
+        *  and requeues internally command to redo.
+        */
+       SYM_QUEHEAD
+               waiting_ccbq;
+       SYM_QUEHEAD
+               started_ccbq;
+       int     num_sgood;
+       u_short started_tags;
+       u_short started_no_tag;
+       u_short started_max;
+       u_short started_limit;
+#endif
+
+#ifdef SYM_OPT_LIMIT_COMMAND_REORDERING
+       /*
+        *  Optionnaly the driver can try to prevent SCSI 
+        *  IOs from being too much reordering.
+        */
+       u_char          tags_si;        /* Current index to tags sum    */
+       u_short         tags_sum[2];    /* Tags sum counters            */
+       u_short         tags_since;     /* # of tags since last switch  */
+#endif
+
+       /*
+        *  Set when we want to clear all tasks.
+        */
+       u_char to_clear;
+
+       /*
+        *  Capabilities.
+        */
+       u_char  user_flags;
+       u_char  curr_flags;
+};
+
+/*
+ *  Action from SCRIPTS on a task.
+ *  Is part of the CCB, but is also used separately to plug 
+ *  error handling action to perform from SCRIPTS.
+ */
+struct sym_actscr {
+       u32     start;          /* Jumped by SCRIPTS after selection    */
+       u32     restart;        /* Jumped by SCRIPTS on relection       */
+};
+
+/*
+ *  Phase mismatch context.
+ *
+ *  It is part of the CCB and is used as parameters for the 
+ *  DATA pointer. We need two contexts to handle correctly the 
+ *  SAVED DATA POINTER.
+ */
+struct sym_pmc {
+       struct  sym_tblmove sg; /* Updated interrupted SG block */
+       u32     ret;            /* SCRIPT return address        */
+};
+
+/*
+ *  LUN control block lookup.
+ *  We use a direct pointer for LUN #0, and a table of 
+ *  pointers which is only allocated for devices that support 
+ *  LUN(s) > 0.
+ */
+#if SYM_CONF_MAX_LUN <= 1
+#define sym_lp(np, tp, lun) (!lun) ? (tp)->lun0p : 0
+#else
+#define sym_lp(np, tp, lun) \
+       (!lun) ? (tp)->lun0p : (tp)->lunmp ? (tp)->lunmp[(lun)] : 0
+#endif
+
+/*
+ *  Status are used by the host and the script processor.
+ *
+ *  The last four bytes (status[4]) are copied to the 
+ *  scratchb register (declared as scr0..scr3) just after the 
+ *  select/reselect, and copied back just after disconnecting.
+ *  Inside the script the XX_REG are used.
+ */
+
+/*
+ *  Last four bytes (script)
+ */
+#define  HX_REG        scr0
+#define  HX_PRT        nc_scr0
+#define  HS_REG        scr1
+#define  HS_PRT        nc_scr1
+#define  SS_REG        scr2
+#define  SS_PRT        nc_scr2
+#define  HF_REG        scr3
+#define  HF_PRT        nc_scr3
+
+/*
+ *  Last four bytes (host)
+ */
+#define  host_xflags   phys.head.status[0]
+#define  host_status   phys.head.status[1]
+#define  ssss_status   phys.head.status[2]
+#define  host_flags    phys.head.status[3]
+
+/*
+ *  Host flags
+ */
+#define HF_IN_PM0      1u
+#define HF_IN_PM1      (1u<<1)
+#define HF_ACT_PM      (1u<<2)
+#define HF_DP_SAVED    (1u<<3)
+#define HF_SENSE       (1u<<4)
+#define HF_EXT_ERR     (1u<<5)
+#define HF_DATA_IN     (1u<<6)
+#ifdef SYM_CONF_IARB_SUPPORT
+#define HF_HINT_IARB   (1u<<7)
+#endif
+
+/*
+ *  More host flags
+ */
+#if    SYM_CONF_DMA_ADDRESSING_MODE == 2
+#define        HX_DMAP_DIRTY   (1u<<7)
+#endif
+
+/*
+ *  Global CCB HEADER.
+ *
+ *  Due to lack of indirect addressing on earlier NCR chips,
+ *  this substructure is copied from the ccb to a global 
+ *  address after selection (or reselection) and copied back 
+ *  before disconnect.
+ *  For SYMBIOS chips that support LOAD/STORE this copy is 
+ *  not needed and thus not performed.
+ */
+
+struct sym_ccbh {
+       /*
+        *  Start and restart SCRIPTS addresses (must be at 0).
+        */
+/*0*/  struct sym_actscr go;
+
+       /*
+        *  SCRIPTS jump address that deal with data pointers.
+        *  'savep' points to the position in the script responsible 
+        *  for the actual transfer of data.
+        *  It's written on reception of a SAVE_DATA_POINTER message.
+        */
+       u32     savep;          /* Jump address to saved data pointer   */
+       u32     lastp;          /* SCRIPTS address at end of data       */
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+       u32     wlastp;
+#endif
+
+       /*
+        *  Status fields.
+        */
+       u8      status[4];
+};
+
+/*
+ *  GET/SET the value of the data pointer used by SCRIPTS.
+ *
+ *  We must distinguish between the LOAD/STORE-based SCRIPTS 
+ *  that use directly the header in the CCB, and the NCR-GENERIC 
+ *  SCRIPTS that use the copy of the header in the HCB.
+ */
+#if    SYM_CONF_GENERIC_SUPPORT
+#define sym_set_script_dp(np, cp, dp)                          \
+       do {                                                    \
+               if (np->features & FE_LDSTR)                    \
+                       cp->phys.head.lastp = cpu_to_scr(dp);   \
+               else                                            \
+                       np->ccb_head.lastp = cpu_to_scr(dp);    \
+       } while (0)
+#define sym_get_script_dp(np, cp)                              \
+       scr_to_cpu((np->features & FE_LDSTR) ?                  \
+               cp->phys.head.lastp : np->ccb_head.lastp)
+#else
+#define sym_set_script_dp(np, cp, dp)                          \
+       do {                                                    \
+               cp->phys.head.lastp = cpu_to_scr(dp);           \
+       } while (0)
+
+#define sym_get_script_dp(np, cp) (cp->phys.head.lastp)
+#endif
+
+/*
+ *  Data Structure Block
+ *
+ *  During execution of a ccb by the script processor, the 
+ *  DSA (data structure address) register points to this 
+ *  substructure of the ccb.
+ */
+struct sym_dsb {
+       /*
+        *  CCB header.
+        *  Also assumed at offset 0 of the sym_ccb structure.
+        */
+/*0*/  struct sym_ccbh head;
+
+       /*
+        *  Phase mismatch contexts.
+        *  We need two to handle correctly the SAVED DATA POINTER.
+        *  MUST BOTH BE AT OFFSET < 256, due to using 8 bit arithmetic 
+        *  for address calculation from SCRIPTS.
+        */
+       struct sym_pmc pm0;
+       struct sym_pmc pm1;
+
+       /*
+        *  Table data for Script
+        */
+       struct sym_tblsel  select;
+       struct sym_tblmove smsg;
+       struct sym_tblmove smsg_ext;
+       struct sym_tblmove cmd;
+       struct sym_tblmove sense;
+       struct sym_tblmove wresid;
+       struct sym_tblmove data [SYM_CONF_MAX_SG];
+};
+
+/*
+ *  Our Command Control Block
+ */
+struct sym_ccb {
+       /*
+        *  This is the data structure which is pointed by the DSA 
+        *  register when it is executed by the script processor.
+        *  It must be the first entry.
+        */
+       struct sym_dsb phys;
+
+       /*
+        *  Pointer to CAM ccb and related stuff.
+        */
+       cam_ccb_p cam_ccb;      /* CAM scsiio ccb               */
+       u8      cdb_buf[16];    /* Copy of CDB                  */
+       u8      *sns_bbuf;      /* Bounce buffer for sense data */
+#ifndef        SYM_SNS_BBUF_LEN
+#define        SYM_SNS_BBUF_LEN (32)
+#endif 
+       int     data_len;       /* Total data length            */
+       int     segments;       /* Number of SG segments        */
+
+       u8      order;          /* Tag type (if tagged command) */
+
+       /*
+        *  Miscellaneous status'.
+        */
+       u_char  nego_status;    /* Negotiation status           */
+       u_char  xerr_status;    /* Extended error flags         */
+       u32     extra_bytes;    /* Extraneous bytes transferred */
+
+       /*
+        *  Message areas.
+        *  We prepare a message to be sent after selection.
+        *  We may use a second one if the command is rescheduled 
+        *  due to CHECK_CONDITION or COMMAND TERMINATED.
+        *  Contents are IDENTIFY and SIMPLE_TAG.
+        *  While negotiating sync or wide transfer,
+        *  a SDTR or WDTR message is appended.
+        */
+       u_char  scsi_smsg [12];
+       u_char  scsi_smsg2[12];
+
+       /*
+        *  Auto request sense related fields.
+        */
+       u_char  sensecmd[6];    /* Request Sense command        */
+       u_char  sv_scsi_status; /* Saved SCSI status            */
+       u_char  sv_xerr_status; /* Saved extended status        */
+       int     sv_resid;       /* Saved residual               */
+
+       /*
+        *  O/S specific data structure.
+        */
+#ifdef SYM_HAVE_SCCB
+       struct sym_sccb s;
+#endif
+       /*
+        *  Other fields.
+        */
+#ifdef SYM_OPT_HANDLE_IO_TIMEOUT
+       SYM_QUEHEAD tmo_linkq;  /* Optional timeout handling    */
+       u_int   tmo_clock;      /* (link and dealine value)     */
+#endif
+       u32     ccb_ba;         /* BUS address of this CCB      */
+       u_short tag;            /* Tag for this transfer        */
+                               /*  NO_TAG means no tag         */
+       u_char  target;
+       u_char  lun;
+       ccb_p   link_ccbh;      /* Host adapter CCB hash chain  */
+       SYM_QUEHEAD
+               link_ccbq;      /* Link to free/busy CCB queue  */
+       u32     startp;         /* Initial data pointer         */
+       u32     goalp;          /* Expected last data pointer   */
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+       u32     wgoalp;
+#endif
+       int     ext_sg;         /* Extreme data pointer, used   */
+       int     ext_ofs;        /*  to calculate the residual.  */
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+       SYM_QUEHEAD
+               link2_ccbq;     /* Link for device queueing     */
+       u_char  started;        /* CCB queued to the squeue     */
+#endif
+       u_char  to_abort;       /* Want this IO to be aborted   */
+#ifdef SYM_OPT_LIMIT_COMMAND_REORDERING
+       u_char  tags_si;        /* Lun tags sum index (0,1)     */
+#endif
+};
+
+#define CCB_BA(cp,lbl) (cp->ccb_ba + offsetof(struct sym_ccb, lbl))
+
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+#define        sym_goalp(cp) ((cp->host_flags & HF_DATA_IN) ? cp->goalp : cp->wgoalp)
+#else
+#define        sym_goalp(cp) (cp->goalp)
+#endif
+
+/*
+ *  Host Control Block
+ */
+struct sym_hcb {
+       /*
+        *  Global headers.
+        *  Due to poorness of addressing capabilities, earlier 
+        *  chips (810, 815, 825) copy part of the data structures 
+        *  (CCB, TCB and LCB) in fixed areas.
+        */
+#if    SYM_CONF_GENERIC_SUPPORT
+       struct sym_ccbh ccb_head;
+       struct sym_tcbh tcb_head;
+       struct sym_lcbh lcb_head;
+#endif
+       /*
+        *  Idle task and invalid task actions and 
+        *  their bus addresses.
+        */
+       struct sym_actscr idletask, notask, bad_itl, bad_itlq;
+       u32 idletask_ba, notask_ba, bad_itl_ba, bad_itlq_ba;
+
+       /*
+        *  Dummy lun table to protect us against target 
+        *  returning bad lun number on reselection.
+        */
+       u32     *badluntbl;     /* Table physical address       */
+       u32     badlun_sa;      /* SCRIPT handler BUS address   */
+
+       /*
+        *  Bus address of this host control block.
+        */
+       u32     hcb_ba;
+
+       /*
+        *  Bit 32-63 of the on-chip RAM bus address in LE format.
+        *  The START_RAM64 script loads the MMRS and MMWS from this 
+        *  field.
+        */
+       u32     scr_ram_seg;
+
+       /*
+        *  Initial value of some IO register bits.
+        *  These values are assumed to have been set by BIOS, and may 
+        *  be used to probe adapter implementation differences.
+        */
+       u_char  sv_scntl0, sv_scntl3, sv_dmode, sv_dcntl, sv_ctest3, sv_ctest4,
+               sv_ctest5, sv_gpcntl, sv_stest2, sv_stest4, sv_scntl4,
+               sv_stest1;
+
+       /*
+        *  Actual initial value of IO register bits used by the 
+        *  driver. They are loaded at initialisation according to  
+        *  features that are to be enabled/disabled.
+        */
+       u_char  rv_scntl0, rv_scntl3, rv_dmode, rv_dcntl, rv_ctest3, rv_ctest4, 
+               rv_ctest5, rv_stest2, rv_ccntl0, rv_ccntl1, rv_scntl4;
+
+       /*
+        *  Target data.
+        */
+       struct sym_tcb  target[SYM_CONF_MAX_TARGET];
+
+       /*
+        *  Target control block bus address array used by the SCRIPT 
+        *  on reselection.
+        */
+       u32             *targtbl;
+       u32             targtbl_ba;
+
+       /*
+        *  DMA pool handle for this HBA.
+        */
+#ifdef SYM_OPT_BUS_DMA_ABSTRACTION
+       m_pool_ident_t  bus_dmat;
+#endif
+
+       /*
+        *  O/S specific data structure
+        */
+       struct sym_shcb s;
+
+       /*
+        *  Physical bus addresses of the chip.
+        */
+       u32             mmio_ba;        /* MMIO 32 bit BUS address      */
+       int             mmio_ws;        /* MMIO Window size             */
+
+       u32             ram_ba;         /* RAM 32 bit BUS address       */
+       int             ram_ws;         /* RAM window size              */
+
+       /*
+        *  SCRIPTS virtual and physical bus addresses.
+        *  'script'  is loaded in the on-chip RAM if present.
+        *  'scripth' stays in main memory for all chips except the 
+        *  53C895A, 53C896 and 53C1010 that provide 8K on-chip RAM.
+        */
+       u_char          *scripta0;      /* Copy of scripts A, B, Z      */
+       u_char          *scriptb0;
+       u_char          *scriptz0;
+       u32             scripta_ba;     /* Actual scripts A, B, Z       */
+       u32             scriptb_ba;     /* 32 bit bus addresses.        */
+       u32             scriptz_ba;
+       u_short         scripta_sz;     /* Actual size of script A, B, Z*/
+       u_short         scriptb_sz;
+       u_short         scriptz_sz;
+
+       /*
+        *  Bus addresses, setup and patch methods for 
+        *  the selected firmware.
+        */
+       struct sym_fwa_ba fwa_bas;      /* Useful SCRIPTA bus addresses */
+       struct sym_fwb_ba fwb_bas;      /* Useful SCRIPTB bus addresses */
+       struct sym_fwz_ba fwz_bas;      /* Useful SCRIPTZ bus addresses */
+       void            (*fw_setup)(hcb_p np, struct sym_fw *fw);
+       void            (*fw_patch)(hcb_p np);
+       char            *fw_name;
+
+       /*
+        *  General controller parameters and configuration.
+        */
+       u_short device_id;      /* PCI device id                */
+       u_char  revision_id;    /* PCI device revision id       */
+       u_int   features;       /* Chip features map            */
+       u_char  myaddr;         /* SCSI id of the adapter       */
+       u_char  maxburst;       /* log base 2 of dwords burst   */
+       u_char  maxwide;        /* Maximum transfer width       */
+       u_char  minsync;        /* Min sync period factor (ST)  */
+       u_char  maxsync;        /* Max sync period factor (ST)  */
+       u_char  maxoffs;        /* Max scsi offset        (ST)  */
+       u_char  minsync_dt;     /* Min sync period factor (DT)  */
+       u_char  maxsync_dt;     /* Max sync period factor (DT)  */
+       u_char  maxoffs_dt;     /* Max scsi offset        (DT)  */
+       u_char  multiplier;     /* Clock multiplier (1,2,4)     */
+       u_char  clock_divn;     /* Number of clock divisors     */
+       u32     clock_khz;      /* SCSI clock frequency in KHz  */
+       u32     pciclk_khz;     /* Estimated PCI clock  in KHz  */
+       /*
+        *  Start queue management.
+        *  It is filled up by the host processor and accessed by the 
+        *  SCRIPTS processor in order to start SCSI commands.
+        */
+       volatile                /* Prevent code optimizations   */
+       u32     *squeue;        /* Start queue virtual address  */
+       u32     squeue_ba;      /* Start queue BUS address      */
+       u_short squeueput;      /* Next free slot of the queue  */
+       u_short actccbs;        /* Number of allocated CCBs     */
+
+       /*
+        *  Command completion queue.
+        *  It is the same size as the start queue to avoid overflow.
+        */
+       u_short dqueueget;      /* Next position to scan        */
+       volatile                /* Prevent code optimizations   */
+       u32     *dqueue;        /* Completion (done) queue      */
+       u32     dqueue_ba;      /* Done queue BUS address       */
+
+       /*
+        *  Miscellaneous buffers accessed by the scripts-processor.
+        *  They shall be DWORD aligned, because they may be read or 
+        *  written with a script command.
+        */
+       u_char          msgout[8];      /* Buffer for MESSAGE OUT       */
+       u_char          msgin [8];      /* Buffer for MESSAGE IN        */
+       u32             lastmsg;        /* Last SCSI message sent       */
+       u32             scratch;        /* Scratch for SCSI receive     */
+                                       /* Also used for cache test     */
+       /*
+        *  Miscellaneous configuration and status parameters.
+        */
+       u_char          usrflags;       /* Miscellaneous user flags     */
+       u_char          scsi_mode;      /* Current SCSI BUS mode        */
+       u_char          verbose;        /* Verbosity for this controller*/
+
+       /*
+        *  CCB lists and queue.
+        */
+       ccb_p *ccbh;                    /* CCBs hashed by DSA value     */
+                                       /* CCB_HASH_SIZE lists of CCBs  */
+       SYM_QUEHEAD     free_ccbq;      /* Queue of available CCBs      */
+       SYM_QUEHEAD     busy_ccbq;      /* Queue of busy CCBs           */
+
+       /*
+        *  During error handling and/or recovery,
+        *  active CCBs that are to be completed with 
+        *  error or requeued are moved from the busy_ccbq
+        *  to the comp_ccbq prior to completion.
+        */
+       SYM_QUEHEAD     comp_ccbq;
+
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+       SYM_QUEHEAD     dummy_ccbq;
+#endif
+       /*
+        *  Optional handling of IO timeouts.
+        */
+#ifdef SYM_OPT_HANDLE_IO_TIMEOUT
+       SYM_QUEHEAD tmo0_ccbq;
+       SYM_QUEHEAD *tmo_ccbq;  /* [2*SYM_TIMEOUT_ORDER_MAX] */
+       u_int   tmo_clock;
+       u_int   tmo_actq;
+#endif
+
+       /*
+        *  IMMEDIATE ARBITRATION (IARB) control.
+        *
+        *  We keep track in 'last_cp' of the last CCB that has been 
+        *  queued to the SCRIPTS processor and clear 'last_cp' when 
+        *  this CCB completes. If last_cp is not zero at the moment 
+        *  we queue a new CCB, we set a flag in 'last_cp' that is 
+        *  used by the SCRIPTS as a hint for setting IARB.
+        *  We donnot set more than 'iarb_max' consecutive hints for 
+        *  IARB in order to leave devices a chance to reselect.
+        *  By the way, any non zero value of 'iarb_max' is unfair. :)
+        */
+#ifdef SYM_CONF_IARB_SUPPORT
+       u_short         iarb_max;       /* Max. # consecutive IARB hints*/
+       u_short         iarb_count;     /* Actual # of these hints      */
+       ccb_p           last_cp;
+#endif
+
+       /*
+        *  Command abort handling.
+        *  We need to synchronize tightly with the SCRIPTS 
+        *  processor in order to handle things correctly.
+        */
+       u_char          abrt_msg[4];    /* Message to send buffer       */
+       struct sym_tblmove abrt_tbl;    /* Table for the MOV of it      */
+       struct sym_tblsel  abrt_sel;    /* Sync params for selection    */
+       u_char          istat_sem;      /* Tells the chip to stop (SEM) */
+
+       /*
+        *  64 bit DMA handling.
+        */
+#if    SYM_CONF_DMA_ADDRESSING_MODE != 0
+       u_char  use_dac;                /* Use PCI DAC cycles           */
+#if    SYM_CONF_DMA_ADDRESSING_MODE == 2
+       u_char  dmap_dirty;             /* Dma segments registers dirty */
+       u32     dmap_bah[SYM_DMAP_SIZE];/* Segment registers map        */
+#endif
+#endif
+};
+
+#define HCB_BA(np, lbl)        (np->hcb_ba + offsetof(struct sym_hcb, lbl))
+
+/*
+ *  NVRAM reading (sym_nvram.c).
+ */
+void sym_nvram_setup_host (hcb_p np, struct sym_nvram *nvram);
+void sym_nvram_setup_target (hcb_p np, int target, struct sym_nvram *nvp);
+int sym_read_nvram (sdev_p np, struct sym_nvram *nvp);
+
+/*
+ *  FIRMWARES (sym_fw.c)
+ */
+struct sym_fw * sym_find_firmware(struct sym_pci_chip *chip);
+void sym_fw_bind_script (hcb_p np, u32 *start, int len);
+
+/*
+ *  Driver methods called from O/S specific code.
+ */
+char *sym_driver_name(void);
+void sym_print_xerr(ccb_p cp, int x_status);
+int sym_reset_scsi_bus(hcb_p np, int enab_int);
+struct sym_pci_chip *
+sym_lookup_pci_chip_table (u_short device_id, u_char revision);
+void sym_put_start_queue(hcb_p np, ccb_p cp);
+#ifdef SYM_OPT_HANDLE_DEVICE_QUEUEING
+void sym_start_next_ccbs(hcb_p np, lcb_p lp, int maxn);
+#endif
+void sym_start_up (hcb_p np, int reason);
+void sym_interrupt (hcb_p np);
+void sym_flush_comp_queue(hcb_p np, int cam_status);
+int sym_clear_tasks(hcb_p np, int cam_status, int target, int lun, int task);
+ccb_p sym_get_ccb (hcb_p np, u_char tn, u_char ln, u_char tag_order);
+void sym_free_ccb (hcb_p np, ccb_p cp);
+lcb_p sym_alloc_lcb (hcb_p np, u_char tn, u_char ln);
+int sym_queue_scsiio(hcb_p np, cam_scsiio_p csio, ccb_p cp);
+int sym_abort_scsiio(hcb_p np, cam_ccb_p ccb, int timed_out);
+int sym_abort_ccb(hcb_p np, ccb_p cp, int timed_out);
+int sym_reset_scsi_target(hcb_p np, int target);
+void sym_hcb_free(hcb_p np);
+
+#ifdef SYM_OPT_NVRAM_PRE_READ
+int sym_hcb_attach(hcb_p np, struct sym_fw *fw, struct sym_nvram *nvram);
+#else
+int sym_hcb_attach(hcb_p np, struct sym_fw *fw);
+#endif
+
+/*
+ *  Optionnaly, the driver may handle IO timeouts.
+ */
+#ifdef SYM_OPT_HANDLE_IO_TIMEOUT
+int sym_abort_ccb(hcb_p np, ccb_p cp, int timed_out);
+void sym_timeout_ccb(hcb_p np, ccb_p cp, u_int ticks);
+static void __inline sym_untimeout_ccb(hcb_p np, ccb_p cp)
+{
+       sym_remque(&cp->tmo_linkq);
+       sym_insque_head(&cp->tmo_linkq, &np->tmo0_ccbq);
+}
+void sym_clock(hcb_p np);
+#endif /* SYM_OPT_HANDLE_IO_TIMEOUT */
+
+/*
+ *  Optionnaly, the driver may provide a function
+ *  to announce transfer rate changes.
+ */
+#ifdef SYM_OPT_ANNOUNCE_TRANSFER_RATE
+void sym_announce_transfer_rate(hcb_p np, int target);
+#endif
+
+/*
+ *  Optionnaly, the driver may sniff inquiry data.
+ */
+#ifdef SYM_OPT_SNIFF_INQUIRY
+#define        INQ7_CMDQ       (0x02)
+#define        INQ7_SYNC       (0x10)
+#define        INQ7_WIDE16     (0x20)
+
+#define INQ56_CLOCKING (3<<2)
+#define INQ56_ST_ONLY  (0<<2)
+#define INQ56_DT_ONLY  (1<<2)
+#define INQ56_ST_DT    (3<<2)
+
+void sym_update_trans_settings(hcb_p np, tcb_p tp);
+int  
+__sym_sniff_inquiry(hcb_p np, u_char tn, u_char ln,
+                    u_char *inq_data, int inq_len);
+#endif
+
+
+/*
+ *  Build a scatter/gather entry.
+ *
+ *  For 64 bit systems, we use the 8 upper bits of the size field 
+ *  to provide bus address bits 32-39 to the SCRIPTS processor.
+ *  This allows the 895A, 896, 1010 to address up to 1 TB of memory.
+ */
+
+#if   SYM_CONF_DMA_ADDRESSING_MODE == 0
+#define sym_build_sge(np, data, badd, len)     \
+do {                                           \
+       (data)->addr = cpu_to_scr(badd);        \
+       (data)->size = cpu_to_scr(len);         \
+} while (0)
+#elif SYM_CONF_DMA_ADDRESSING_MODE == 1
+#define sym_build_sge(np, data, badd, len)                             \
+do {                                                                   \
+       (data)->addr = cpu_to_scr(badd);                                \
+       (data)->size = cpu_to_scr((((badd) >> 8) & 0xff000000) + len);  \
+} while (0)
+#elif SYM_CONF_DMA_ADDRESSING_MODE == 2
+int sym_lookup_dmap(hcb_p np, u32 h, int s);
+static __inline void 
+sym_build_sge(hcb_p np, struct sym_tblmove *data, u64 badd, int len)
+{
+       u32 h = (badd>>32);
+       int s = (h&SYM_DMAP_MASK);
+
+       if (h != np->dmap_bah[s])
+               goto bad;
+good:
+       (data)->addr = cpu_to_scr(badd);
+       (data)->size = cpu_to_scr((s<<24) + len);
+       return;
+bad:
+       s = sym_lookup_dmap(np, h, s);
+       goto good;
+}
+#else
+#error "Unsupported DMA addressing mode"
+#endif
+
+/*
+ *  Set up data pointers used by SCRIPTS.
+ *  Called from O/S specific code.
+ */
+static void __inline 
+sym_setup_data_pointers(hcb_p np, ccb_p cp, int dir)
+{
+       u32 lastp, goalp;
+
+       /*
+        *  No segments means no data.
+        */
+       if (!cp->segments)
+               dir = CAM_DIR_NONE;
+
+       /*
+        *  Set the data pointer.
+        */
+       switch(dir) {
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+       case CAM_DIR_UNKNOWN:
+#endif
+       case CAM_DIR_OUT:
+               goalp = SCRIPTA_BA (np, data_out2) + 8;
+               lastp = goalp - 8 - (cp->segments * (2*4));
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+               cp->wgoalp = cpu_to_scr(goalp);
+               if (dir != CAM_DIR_UNKNOWN)
+                       break;
+               cp->phys.head.wlastp = cpu_to_scr(lastp);
+               /* fall through */
+#else
+               break;
+#endif
+       case CAM_DIR_IN:
+               cp->host_flags |= HF_DATA_IN;
+               goalp = SCRIPTA_BA (np, data_in2) + 8;
+               lastp = goalp - 8 - (cp->segments * (2*4));
+               break;
+       case CAM_DIR_NONE:
+       default:
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+               cp->host_flags |= HF_DATA_IN;
+#endif
+               lastp = goalp = SCRIPTB_BA (np, no_data);
+               break;
+       }
+
+       /*
+        *  Set all pointers values needed by SCRIPTS.
+        */
+       cp->phys.head.lastp = cpu_to_scr(lastp);
+       cp->phys.head.savep = cpu_to_scr(lastp);
+       cp->startp          = cp->phys.head.savep;
+       cp->goalp           = cpu_to_scr(goalp);
+
+#ifdef SYM_OPT_HANDLE_DIR_UNKNOWN
+       /*
+        *  If direction is unknown, start at data_io.
+        */
+       if (dir == CAM_DIR_UNKNOWN)
+               cp->phys.head.savep = cpu_to_scr(SCRIPTB_BA (np, data_io));
+#endif
+}
+
+/*
+ *  MEMORY ALLOCATOR.
+ */
+
+/*
+ *  Shortest memory chunk is (1<<SYM_MEM_SHIFT), currently 16.
+ *  Actual allocations happen as SYM_MEM_CLUSTER_SIZE sized.
+ *  (1 PAGE at a time is just fine).
+ */
+#define SYM_MEM_SHIFT  4
+#define SYM_MEM_CLUSTER_SIZE   (1UL << SYM_MEM_CLUSTER_SHIFT)
+#define SYM_MEM_CLUSTER_MASK   (SYM_MEM_CLUSTER_SIZE-1)
+
+/*
+ *  Link between free memory chunks of a given size.
+ */
+typedef struct sym_m_link {
+       struct sym_m_link *next;
+} *m_link_p;
+
+/*
+ *  Virtual to bus physical translation for a given cluster.
+ *  Such a structure is only useful with DMA abstraction.
+ */
+#ifdef SYM_OPT_BUS_DMA_ABSTRACTION
+typedef struct sym_m_vtob {    /* Virtual to Bus address translation */
+       struct sym_m_vtob *next;
+#ifdef SYM_HAVE_M_SVTOB
+       struct sym_m_svtob s;   /* OS specific data structure */
+#endif
+       m_addr_t        vaddr;  /* Virtual address */
+       m_addr_t        baddr;  /* Bus physical address */
+} *m_vtob_p;
+
+/* Hash this stuff a bit to speed up translations */
+#define VTOB_HASH_SHIFT                5
+#define VTOB_HASH_SIZE         (1UL << VTOB_HASH_SHIFT)
+#define VTOB_HASH_MASK         (VTOB_HASH_SIZE-1)
+#define VTOB_HASH_CODE(m)      \
+       ((((m_addr_t) (m)) >> SYM_MEM_CLUSTER_SHIFT) & VTOB_HASH_MASK)
+#endif /* SYM_OPT_BUS_DMA_ABSTRACTION */
+
+/*
+ *  Memory pool of a given kind.
+ *  Ideally, we want to use:
+ *  1) 1 pool for memory we donnot need to involve in DMA.
+ *  2) The same pool for controllers that require same DMA 
+ *     constraints and features.
+ *     The OS specific m_pool_id_t thing and the sym_m_pool_match() 
+ *     method are expected to tell the driver about.
+ */
+typedef struct sym_m_pool {
+#ifdef SYM_OPT_BUS_DMA_ABSTRACTION
+       m_pool_ident_t  dev_dmat;       /* Identifies the pool (see above) */
+       m_addr_t (*get_mem_cluster)(struct sym_m_pool *);
+#ifdef SYM_MEM_FREE_UNUSED
+       void (*free_mem_cluster)(struct sym_m_pool *, m_addr_t);
+#endif
+#define M_GET_MEM_CLUSTER()            mp->get_mem_cluster(mp)
+#define M_FREE_MEM_CLUSTER(p)          mp->free_mem_cluster(mp, p)
+#ifdef SYM_HAVE_M_SPOOL
+       struct sym_m_spool      s;      /* OS specific data structure */
+#endif
+       int nump;
+       m_vtob_p vtob[VTOB_HASH_SIZE];
+       struct sym_m_pool *next;
+#else
+#define M_GET_MEM_CLUSTER()            sym_get_mem_cluster()
+#define M_FREE_MEM_CLUSTER(p)          sym_free_mem_cluster(p)
+#endif /* SYM_OPT_BUS_DMA_ABSTRACTION */
+       struct sym_m_link h[SYM_MEM_CLUSTER_SHIFT - SYM_MEM_SHIFT + 1];
+} *m_pool_p;
+
+/*
+ *  Alloc and free non DMAable memory.
+ */
+void sym_mfree_unlocked(void *ptr, int size, char *name);
+void *sym_calloc_unlocked(int size, char *name);
+
+/*
+ *  Alloc, free and translate addresses to bus physical 
+ *  for DMAable memory.
+ */
+#ifdef SYM_OPT_BUS_DMA_ABSTRACTION
+void *__sym_calloc_dma_unlocked(m_pool_ident_t dev_dmat, int size, char *name);
+void 
+__sym_mfree_dma_unlocked(m_pool_ident_t dev_dmat, void *m,int size, char *name);
+u32 __vtobus_unlocked(m_pool_ident_t dev_dmat, void *m);
+#endif
+
+/*
+ * Verbs used by the driver code for DMAable memory handling.
+ * The _uvptv_ macro avoids a nasty warning about pointer to volatile 
+ * being discarded.
+ */
+#define _uvptv_(p) ((void *)((u_long)(p)))
+
+#define _sym_calloc_dma(np, l, n)      __sym_calloc_dma(np->bus_dmat, l, n)
+#define _sym_mfree_dma(np, p, l, n)    \
+                       __sym_mfree_dma(np->bus_dmat, _uvptv_(p), l, n)
+#define sym_calloc_dma(l, n)           _sym_calloc_dma(np, l, n)
+#define sym_mfree_dma(p, l, n)         _sym_mfree_dma(np, p, l, n)
+#define _vtobus(np, p)                 __vtobus(np->bus_dmat, _uvptv_(p))
+#define vtobus(p)                      _vtobus(np, p)
+
+/*
+ *  Override some function names.
+ */
+#define PRINT_ADDR     sym_print_addr
+#define PRINT_TARGET   sym_print_target
+#define PRINT_LUN      sym_print_lun
+#define MDELAY         sym_mdelay
+#define UDELAY         sym_udelay
+
+#endif /* SYM_HIPD_H */
diff --git a/xen/drivers/scsi/sym53c8xx_2/sym_malloc.c b/xen/drivers/scsi/sym53c8xx_2/sym_malloc.c
new file mode 100644 (file)
index 0000000..7f32ed4
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000  Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ *         Wolfgang Stanglmeier        <wolf@cologne.de>
+ *         Stefan Esser                <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994  Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef __FreeBSD__
+#include <dev/sym/sym_glue.h>
+#else
+#include "sym_glue.h"
+#endif
+
+/*
+ *  Simple power of two buddy-like generic allocator.
+ *  Provides naturally aligned memory chunks.
+ *
+ *  This simple code is not intended to be fast, but to 
+ *  provide power of 2 aligned memory allocations.
+ *  Since the SCRIPTS processor only supplies 8 bit arithmetic, 
+ *  this allocator allows simple and fast address calculations  
+ *  from the SCRIPTS code. In addition, cache line alignment 
+ *  is guaranteed for power of 2 cache line size.
+ *
+ *  This allocator has been developped for the Linux sym53c8xx  
+ *  driver, since this O/S does not provide naturally aligned 
+ *  allocations.
+ *  It has the advantage of allowing the driver to use private 
+ *  pages of memory that will be useful if we ever need to deal 
+ *  with IO MMUs for PCI.
+ */
+static void *___sym_malloc(m_pool_p mp, int size)
+{
+       int i = 0;
+       int s = (1 << SYM_MEM_SHIFT);
+       int j;
+       m_addr_t a;
+       m_link_p h = mp->h;
+
+       if (size > SYM_MEM_CLUSTER_SIZE)
+               return 0;
+
+       while (size > s) {
+               s <<= 1;
+               ++i;
+       }
+
+       j = i;
+       while (!h[j].next) {
+               if (s == SYM_MEM_CLUSTER_SIZE) {
+                       h[j].next = (m_link_p) M_GET_MEM_CLUSTER();
+                       if (h[j].next)
+                               h[j].next->next = 0;
+                       break;
+               }
+               ++j;
+               s <<= 1;
+       }
+       a = (m_addr_t) h[j].next;
+       if (a) {
+               h[j].next = h[j].next->next;
+               while (j > i) {
+                       j -= 1;
+                       s >>= 1;
+                       h[j].next = (m_link_p) (a+s);
+                       h[j].next->next = 0;
+               }
+       }
+#ifdef DEBUG
+       printf("___sym_malloc(%d) = %p\n", size, (void *) a);
+#endif
+       return (void *) a;
+}
+
+/*
+ *  Counter-part of the generic allocator.
+ */
+static void ___sym_mfree(m_pool_p mp, void *ptr, int size)
+{
+       int i = 0;
+       int s = (1 << SYM_MEM_SHIFT);
+       m_link_p q;
+       m_addr_t a, b;
+       m_link_p h = mp->h;
+
+#ifdef DEBUG
+       printf("___sym_mfree(%p, %d)\n", ptr, size);
+#endif
+
+       if (size > SYM_MEM_CLUSTER_SIZE)
+               return;
+
+       while (size > s) {
+               s <<= 1;
+               ++i;
+       }
+
+       a = (m_addr_t) ptr;
+
+       while (1) {
+               if (s == SYM_MEM_CLUSTER_SIZE) {
+#ifdef SYM_MEM_FREE_UNUSED
+                       M_FREE_MEM_CLUSTER(a);
+#else
+                       ((m_link_p) a)->next = h[i].next;
+                       h[i].next = (m_link_p) a;
+#endif
+                       break;
+               }
+               b = a ^ s;
+               q = &h[i];
+               while (q->next && q->next != (m_link_p) b) {
+                       q = q->next;
+               }
+               if (!q->next) {
+                       ((m_link_p) a)->next = h[i].next;
+                       h[i].next = (m_link_p) a;
+                       break;
+               }
+               q->next = q->next->next;
+               a = a & b;
+               s <<= 1;
+               ++i;
+       }
+}
+
+/*
+ *  Verbose and zeroing allocator that wrapps to the generic allocator.
+ */
+static void *__sym_calloc2(m_pool_p mp, int size, char *name, int uflags)
+{
+       void *p;
+
+       p = ___sym_malloc(mp, size);
+
+       if (DEBUG_FLAGS & DEBUG_ALLOC) {
+               printf ("new %-10s[%4d] @%p.\n", name, size, p);
+       }
+
+       if (p)
+               bzero(p, size);
+       else if (uflags & SYM_MEM_WARN)
+               printf ("__sym_calloc2: failed to allocate %s[%d]\n", name, size);
+       return p;
+}
+#define __sym_calloc(mp, s, n) __sym_calloc2(mp, s, n, SYM_MEM_WARN)
+
+/*
+ *  Its counter-part.
+ */
+static void __sym_mfree(m_pool_p mp, void *ptr, int size, char *name)
+{
+       if (DEBUG_FLAGS & DEBUG_ALLOC)
+               printf ("freeing %-10s[%4d] @%p.\n", name, size, ptr);
+
+       ___sym_mfree(mp, ptr, size);
+}
+
+/*
+ *  Default memory pool we donnot need to involve in DMA.
+ *
+ *  If DMA abtraction is not needed, the generic allocator 
+ *  calls directly some kernel allocator.
+ *
+ *  With DMA abstraction, we use functions (methods), to 
+ *  distinguish between non DMAable memory and DMAable memory.
+ */
+#ifndef        SYM_OPT_BUS_DMA_ABSTRACTION
+
+static struct sym_m_pool mp0;
+
+#else
+
+static m_addr_t ___mp0_get_mem_cluster(m_pool_p mp)
+{
+       m_addr_t m = (m_addr_t) sym_get_mem_cluster();
+       if (m)
+               ++mp->nump;
+       return m;
+}
+
+#ifdef SYM_MEM_FREE_UNUSED
+static void ___mp0_free_mem_cluster(m_pool_p mp, m_addr_t m)
+{
+       sym_free_mem_cluster(m);
+       --mp->nump;
+}
+#endif
+
+#ifdef SYM_MEM_FREE_UNUSED
+static struct sym_m_pool mp0 =
+       {0, ___mp0_get_mem_cluster, ___mp0_free_mem_cluster};
+#else
+static struct sym_m_pool mp0 =
+       {0, ___mp0_get_mem_cluster};
+#endif
+
+#endif /* SYM_OPT_BUS_DMA_ABSTRACTION */
+
+/*
+ * Actual memory allocation routine for non-DMAed memory.
+ */
+void *sym_calloc_unlocked(int size, char *name)
+{
+       void *m;
+       m = __sym_calloc(&mp0, size, name);
+       return m;
+}
+
+/*
+ *  Its counter-part.
+ */
+void sym_mfree_unlocked(void *ptr, int size, char *name)
+{
+       __sym_mfree(&mp0, ptr, size, name);
+}
+
+#ifdef SYM_OPT_BUS_DMA_ABSTRACTION
+/*
+ *  Methods that maintains DMAable pools according to user allocations.
+ *  New pools are created on the fly when a new pool id is provided.
+ *  They are deleted on the fly when they get emptied.
+ */
+/* Get a memory cluster that matches the DMA contraints of a given pool */
+static m_addr_t ___get_dma_mem_cluster(m_pool_p mp)
+{
+       m_vtob_p vbp;
+       m_addr_t vaddr;
+
+       vbp = __sym_calloc(&mp0, sizeof(*vbp), "VTOB");
+       if (!vbp)
+               goto out_err;
+
+       vaddr = sym_m_get_dma_mem_cluster(mp, vbp);
+       if (vaddr) {
+               int hc = VTOB_HASH_CODE(vaddr);
+               vbp->next = mp->vtob[hc];
+               mp->vtob[hc] = vbp;
+               ++mp->nump;
+               return (m_addr_t) vaddr;
+       }
+       return vaddr;
+out_err:
+       return 0;
+}
+
+#ifdef SYM_MEM_FREE_UNUSED
+/* Free a memory cluster and associated resources for DMA */
+static void ___free_dma_mem_cluster(m_pool_p mp, m_addr_t m)
+{
+       m_vtob_p *vbpp, vbp;
+       int hc = VTOB_HASH_CODE(m);
+
+       vbpp = &mp->vtob[hc];
+       while (*vbpp && (*vbpp)->vaddr != m)
+               vbpp = &(*vbpp)->next;
+       if (*vbpp) {
+               vbp = *vbpp;
+               *vbpp = (*vbpp)->next;
+               sym_m_free_dma_mem_cluster(mp, vbp);
+               __sym_mfree(&mp0, vbp, sizeof(*vbp), "VTOB");
+               --mp->nump;
+       }
+}
+#endif
+
+/* Fetch the memory pool for a given pool id (i.e. DMA constraints) */
+static __inline m_pool_p ___get_dma_pool(m_pool_ident_t dev_dmat)
+{
+       m_pool_p mp;
+       for (mp = mp0.next;
+               mp && !sym_m_pool_match(mp->dev_dmat, dev_dmat);
+                       mp = mp->next);
+       return mp;
+}
+
+/* Create a new memory DMAable pool (when fetch failed) */
+static m_pool_p ___cre_dma_pool(m_pool_ident_t dev_dmat)
+{
+       m_pool_p mp = 0;
+
+       mp = __sym_calloc(&mp0, sizeof(*mp), "MPOOL");
+       if (mp) {
+               mp->dev_dmat = dev_dmat;
+               if (!sym_m_create_dma_mem_tag(mp)) {
+                       mp->get_mem_cluster = ___get_dma_mem_cluster;
+#ifdef SYM_MEM_FREE_UNUSED
+                       mp->free_mem_cluster = ___free_dma_mem_cluster;
+#endif
+                       mp->next = mp0.next;
+                       mp0.next = mp;
+                       return mp;
+               }
+       }
+       if (mp)
+               __sym_mfree(&mp0, mp, sizeof(*mp), "MPOOL");
+       return 0;
+}
+
+#ifdef SYM_MEM_FREE_UNUSED
+/* Destroy a DMAable memory pool (when got emptied) */
+static void ___del_dma_pool(m_pool_p p)
+{
+       m_pool_p *pp = &mp0.next;
+
+       while (*pp && *pp != p)
+               pp = &(*pp)->next;
+       if (*pp) {
+               *pp = (*pp)->next;
+               sym_m_delete_dma_mem_tag(p);
+               __sym_mfree(&mp0, p, sizeof(*p), "MPOOL");
+       }
+}
+#endif
+
+/*
+ *  Actual allocator for DMAable memory.
+ */
+void *__sym_calloc_dma_unlocked(m_pool_ident_t dev_dmat, int size, char *name)
+{
+       m_pool_p mp;
+       void *m = 0;
+
+       mp = ___get_dma_pool(dev_dmat);
+       if (!mp)
+               mp = ___cre_dma_pool(dev_dmat);
+       if (mp)
+               m = __sym_calloc(mp, size, name);
+#ifdef SYM_MEM_FREE_UNUSED
+       if (mp && !mp->nump)
+               ___del_dma_pool(mp);
+#endif
+
+       return m;
+}
+
+/*
+ *  Its counter-part.
+ */
+void 
+__sym_mfree_dma_unlocked(m_pool_ident_t dev_dmat, void *m, int size, char *name)
+{
+       m_pool_p mp;
+
+       mp = ___get_dma_pool(dev_dmat);
+       if (mp)
+               __sym_mfree(mp, m, size, name);
+#ifdef SYM_MEM_FREE_UNUSED
+       if (mp && !mp->nump)
+               ___del_dma_pool(mp);
+#endif
+}
+
+/*
+ *  Actual virtual to bus physical address translator 
+ *  for 32 bit addressable DMAable memory.
+ */
+u32 __vtobus_unlocked(m_pool_ident_t dev_dmat, void *m)
+{
+       m_pool_p mp;
+       int hc = VTOB_HASH_CODE(m);
+       m_vtob_p vp = 0;
+       m_addr_t a = ((m_addr_t) m) & ~SYM_MEM_CLUSTER_MASK;
+
+       mp = ___get_dma_pool(dev_dmat);
+       if (mp) {
+               vp = mp->vtob[hc];
+               while (vp && (m_addr_t) vp->vaddr != a)
+                       vp = vp->next;
+       }
+       if (!vp)
+               panic("sym: VTOBUS FAILED!\n");
+       return (u32)(vp ? vp->baddr + (((m_addr_t) m) - a) : 0);
+}
+
+#endif /* SYM_OPT_BUS_DMA_ABSTRACTION */
diff --git a/xen/drivers/scsi/sym53c8xx_2/sym_misc.c b/xen/drivers/scsi/sym53c8xx_2/sym_misc.c
new file mode 100644 (file)
index 0000000..8ab37a5
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000  Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ *         Wolfgang Stanglmeier        <wolf@cologne.de>
+ *         Stefan Esser                <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994  Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef __FreeBSD__
+#include <dev/sym/sym_glue.h>
+#else
+#include "sym_glue.h"
+#endif
+
+#ifdef SYM_OPT_HANDLE_IO_TIMEOUT
+/*
+ *  Optional CCB timeout handling.
+ *
+ *  This code is useful for O/Ses that allow or expect 
+ *  SIMs (low-level drivers) to handle SCSI IO timeouts.
+ *  It uses a power-of-two based algorithm of my own:) 
+ *  that avoids scanning of lists, provided that:
+ *
+ *  - The IO does complete in less than half the associated
+ *    timeout value.
+ *  - The greatest delay between the queuing of the IO and 
+ *    its completion is less than 
+ *          (1<<(SYM_CONF_TIMEOUT_ORDER_MAX-1))/2 ticks.
+ *
+ *  For example, if tick is 1 second and the max order is 8,
+ *  any IO that is completed within less than 64 seconds will 
+ *  just be put into some list at queuing and be removed 
+ *  at completion without any additionnal overhead.
+ */
+
+/*
+ *  Set a timeout condition on a CCB.
+ */ 
+void sym_timeout_ccb(hcb_p np, ccb_p cp, u_int ticks)
+{
+       sym_remque(&cp->tmo_linkq);
+       cp->tmo_clock = np->tmo_clock + ticks;
+       if (!ticks) {
+               sym_insque_head(&cp->tmo_linkq, &np->tmo0_ccbq);
+       }
+       else {
+               int i = SYM_CONF_TIMEOUT_ORDER_MAX - 1;
+               while (i > 0) {
+                       if (ticks >= (1<<(i+1)))
+                               break;
+                       --i;
+               }
+               if (!(np->tmo_actq & (1<<i)))
+                       i += SYM_CONF_TIMEOUT_ORDER_MAX;
+               sym_insque_head(&cp->tmo_linkq, &np->tmo_ccbq[i]);
+       }
+}
+
+/*
+ *  Walk a list of CCB and handle timeout conditions.
+ *  Should never be called in normal situations.
+ */
+static void sym_walk_ccb_tmo_list(hcb_p np, SYM_QUEHEAD *tmoq)
+{
+       SYM_QUEHEAD qtmp, *qp;
+       ccb_p cp;
+
+       sym_que_move(tmoq, &qtmp);
+       while ((qp = sym_remque_head(&qtmp)) != 0) {
+               sym_insque_head(qp, &np->tmo0_ccbq);
+               cp = sym_que_entry(qp, struct sym_ccb, tmo_linkq);
+               if (cp->tmo_clock     != np->tmo_clock &&
+                   cp->tmo_clock + 1 != np->tmo_clock)
+                       sym_timeout_ccb(np, cp, cp->tmo_clock - np->tmo_clock);
+               else
+                       sym_abort_ccb(np, cp, 1);
+       }
+}
+
+/*
+ * Our clock handler called from the O/S specific side.
+ */
+void sym_clock(hcb_p np)
+{
+       int i, j;
+       u_int tmp;
+
+       tmp = np->tmo_clock;
+       tmp ^= (++np->tmo_clock);
+
+       for (i = 0; i < SYM_CONF_TIMEOUT_ORDER_MAX; i++, tmp >>= 1) {
+               if (!(tmp & 1))
+                       continue;
+               j = i;
+               if (np->tmo_actq & (1<<i))
+                       j += SYM_CONF_TIMEOUT_ORDER_MAX;
+
+               if (!sym_que_empty(&np->tmo_ccbq[j])) {
+                       sym_walk_ccb_tmo_list(np, &np->tmo_ccbq[j]);
+               }
+               np->tmo_actq ^= (1<<i);
+       }
+}
+#endif /* SYM_OPT_HANDLE_IO_TIMEOUT */
+
+
+#ifdef SYM_OPT_ANNOUNCE_TRANSFER_RATE
+/*
+ *  Announce transfer rate if anything changed since last announcement.
+ */
+void sym_announce_transfer_rate(hcb_p np, int target)
+{
+       tcb_p tp = &np->target[target];
+
+#define __tprev        tp->tinfo.prev
+#define __tcurr        tp->tinfo.curr
+
+       if (__tprev.options  == __tcurr.options &&
+           __tprev.width    == __tcurr.width   &&
+           __tprev.offset   == __tcurr.offset  &&
+           !(__tprev.offset && __tprev.period != __tcurr.period))
+               return;
+
+       __tprev.options  = __tcurr.options;
+       __tprev.width    = __tcurr.width;
+       __tprev.offset   = __tcurr.offset;
+       __tprev.period   = __tcurr.period;
+
+       if (__tcurr.offset && __tcurr.period) {
+               u_int period, f10, mb10;
+               char *scsi;
+
+               period = f10 = mb10 = 0;
+               scsi = "FAST-5";
+
+               if (__tcurr.period <= 9) {
+                       scsi = "FAST-80";
+                       period = 125;
+                       mb10 = 1600;
+               }
+               else {
+                       if      (__tcurr.period <= 11) {
+                               scsi = "FAST-40";
+                               period = 250;
+                               if (__tcurr.period == 11)
+                                       period = 303;
+                       }
+                       else if (__tcurr.period < 25) {
+                               scsi = "FAST-20";
+                               if (__tcurr.period == 12)
+                                       period = 500;
+                       }
+                       else if (__tcurr.period <= 50) {
+                               scsi = "FAST-10";
+                       }
+                       if (!period)
+                               period = 40 * __tcurr.period;
+                       f10 = 100000 << (__tcurr.width ? 1 : 0);
+                       mb10 = (f10 + period/2) / period;
+               }
+               printf_info (
+                   "%s:%d: %s %sSCSI %d.%d MB/s %s (%d.%d ns, offset %d)\n",
+                   sym_name(np), target, scsi, __tcurr.width? "WIDE " : "",
+                   mb10/10, mb10%10,
+                   (__tcurr.options & PPR_OPT_DT) ? "DT" : "ST",
+                   period/10, period%10, __tcurr.offset);
+       }
+       else
+               printf_info ("%s:%d: %sasynchronous.\n", 
+                            sym_name(np), target, __tcurr.width? "wide " : "");
+}
+#undef __tprev
+#undef __tcurr
+#endif /* SYM_OPT_ANNOUNCE_TRANSFER_RATE */
+
+
+#ifdef SYM_OPT_SNIFF_INQUIRY
+/*
+ *  Update transfer settings according to user settings 
+ *  and bits sniffed out from INQUIRY response.
+ */
+void sym_update_trans_settings(hcb_p np, tcb_p tp)
+{
+       bcopy(&tp->tinfo.user, &tp->tinfo.goal, sizeof(tp->tinfo.goal));
+
+       if (tp->inq_version >= 4) {
+               switch(tp->inq_byte56 & INQ56_CLOCKING) {
+               case INQ56_ST_ONLY:
+                       tp->tinfo.goal.options = 0;
+                       break;
+               case INQ56_DT_ONLY:
+               case INQ56_ST_DT:
+               default:
+                       break;
+               }
+       }
+
+       if (!((tp->inq_byte7 & tp->inq_byte7_valid) & INQ7_WIDE16)) {
+               tp->tinfo.goal.width   = 0;
+               tp->tinfo.goal.options = 0;
+       }
+
+       if (!((tp->inq_byte7 & tp->inq_byte7_valid) & INQ7_SYNC)) {
+               tp->tinfo.goal.offset  = 0;
+               tp->tinfo.goal.options = 0;
+       }
+
+       if (tp->tinfo.goal.options & PPR_OPT_DT) {
+               if (tp->tinfo.goal.offset > np->maxoffs_dt)
+                       tp->tinfo.goal.offset = np->maxoffs_dt;
+       }
+       else {
+               if (tp->tinfo.goal.offset > np->maxoffs)
+                       tp->tinfo.goal.offset = np->maxoffs;
+       }
+}
+
+/*
+ *  Snoop target capabilities from INQUIRY response.
+ *  We only believe device versions >= SCSI-2 that use 
+ *  appropriate response data format (2). But it seems 
+ *  that some CCS devices also support SYNC (?).
+ */
+int 
+__sym_sniff_inquiry(hcb_p np, u_char tn, u_char ln,
+                    u_char *inq_data, int inq_len)
+{
+       tcb_p tp = &np->target[tn];
+       u_char inq_version;
+       u_char inq_byte7;
+       u_char inq_byte56;
+
+       if (!inq_data || inq_len < 2)
+               return -1;
+
+       /*
+        *  Check device type and qualifier.
+        */
+       if ((inq_data[0] & 0xe0) == 0x60)
+               return -1;
+
+       /*
+        *  Get SPC version.
+        */
+       if (inq_len <= 2)
+               return -1;
+       inq_version = inq_data[2] & 0x7;
+
+       /*
+        *  Get SYNC/WIDE16 capabilities.
+        */
+       inq_byte7 = tp->inq_byte7;
+       if (inq_version >= 2 && (inq_data[3] & 0xf) == 2) {
+               if (inq_len > 7)
+                       inq_byte7 = inq_data[7];
+       }
+       else if (inq_version == 1 && (inq_data[3] & 0xf) == 1)
+               inq_byte7 = INQ7_SYNC;
+
+       /*
+        *  Get Tagged Command Queuing capability.
+        */
+       if (inq_byte7 & INQ7_CMDQ)
+               sym_set_bit(tp->cmdq_map, ln);
+       else
+               sym_clr_bit(tp->cmdq_map, ln);
+       inq_byte7 &= ~INQ7_CMDQ;
+
+       /*
+        *  Get CLOCKING capability.
+        */
+       inq_byte56 = tp->inq_byte56;
+       if (inq_version >= 4 && inq_len > 56)
+               tp->inq_byte56 = inq_data[56];
+#if 0
+printf("XXXXXX [%d] inq_version=%x inq_byte7=%x inq_byte56=%x XXXXX\n",
+       inq_len, inq_version, inq_byte7, inq_byte56);
+#endif
+       /*
+        *  Trigger a negotiation if needed.
+        */
+       if (tp->inq_version != inq_version ||
+           tp->inq_byte7   != inq_byte7   ||
+           tp->inq_byte56  != inq_byte56) {
+               tp->inq_version = inq_version;
+               tp->inq_byte7   = inq_byte7;
+               tp->inq_byte56  = inq_byte56;
+               return 1;
+       }
+       return 0;
+}
+#endif /* SYM_OPT_SNIFF_INQUIRY */
diff --git a/xen/drivers/scsi/sym53c8xx_2/sym_misc.h b/xen/drivers/scsi/sym53c8xx_2/sym_misc.h
new file mode 100644 (file)
index 0000000..3a4b41f
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000  Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ *         Wolfgang Stanglmeier        <wolf@cologne.de>
+ *         Stefan Esser                <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994  Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef SYM_MISC_H
+#define SYM_MISC_H
+
+/*
+ *  A 'read barrier' flushes any data that have been prefetched 
+ *  by the processor due to out of order execution. Such a barrier 
+ *  must notably be inserted prior to looking at data that have 
+ *  been DMAed, assuming that program does memory READs in proper 
+ *  order and that the device ensured proper ordering of WRITEs.
+ *
+ *  A 'write barrier' prevents any previous WRITEs to pass further 
+ *  WRITEs. Such barriers must be inserted each time another agent 
+ *  relies on ordering of WRITEs.
+ *
+ *  Note that, due to posting of PCI memory writes, we also must 
+ *  insert dummy PCI read transactions when some ordering involving 
+ *  both directions over the PCI does matter. PCI transactions are 
+ *  fully ordered in each direction.
+ *
+ *  IA32 processors insert implicit barriers when the processor 
+ *  accesses unchacheable either for reading or writing, and 
+ *  donnot reorder WRITEs. As a result, some 'read barriers' can 
+ *  be avoided (following access to uncacheable), and 'write 
+ *  barriers' should be useless (preventing compiler optimizations 
+ *  should be enough).
+ */
+
+#if    defined __i386__
+#define __READ_BARRIER()       \
+               __asm__ volatile("lock; addl $0,0(%%esp)": : :"memory")
+#define __WRITE_BARRIER()      __asm__ volatile ("": : :"memory")
+#elif  defined __powerpc__
+#define __READ_BARRIER()       __asm__ volatile("eieio; sync" : : : "memory")
+#define __WRITE_BARRIER()      __asm__ volatile("eieio; sync" : : : "memory")
+#elif  defined __ia64__
+#define __READ_BARRIER()       __asm__ volatile("mf.a; mf" : : : "memory")
+#define __WRITE_BARRIER()      __asm__ volatile("mf.a; mf" : : : "memory")
+#elif  defined __alpha__
+#define __READ_BARRIER()       __asm__ volatile("mb": : :"memory")
+#define __WRITE_BARRIER()      __asm__ volatile("mb": : :"memory")
+#else
+#define __READ_BARRIER()       mb()
+#define __WRITE_BARRIER()      mb()
+#endif
+
+#ifndef MEMORY_READ_BARRIER
+#define MEMORY_READ_BARRIER()  __READ_BARRIER()
+#endif
+#ifndef MEMORY_WRITE_BARRIER
+#define MEMORY_WRITE_BARRIER() __WRITE_BARRIER()
+#endif
+
+
+/*
+ *  A la VMS/CAM-3 queue management.
+ */
+typedef struct sym_quehead {
+       struct sym_quehead *flink;      /* Forward  pointer */
+       struct sym_quehead *blink;      /* Backward pointer */
+} SYM_QUEHEAD;
+
+#define sym_que_init(ptr) do { \
+       (ptr)->flink = (ptr); (ptr)->blink = (ptr); \
+} while (0)
+
+static __inline struct sym_quehead *sym_que_first(struct sym_quehead *head)
+{
+       return (head->flink == head) ? 0 : head->flink;
+}
+
+static __inline struct sym_quehead *sym_que_last(struct sym_quehead *head)
+{
+       return (head->blink == head) ? 0 : head->blink;
+}
+
+static __inline void __sym_que_add(struct sym_quehead * new,
+       struct sym_quehead * blink,
+       struct sym_quehead * flink)
+{
+       flink->blink    = new;
+       new->flink      = flink;
+       new->blink      = blink;
+       blink->flink    = new;
+}
+
+static __inline void __sym_que_del(struct sym_quehead * blink,
+       struct sym_quehead * flink)
+{
+       flink->blink = blink;
+       blink->flink = flink;
+}
+
+static __inline int sym_que_empty(struct sym_quehead *head)
+{
+       return head->flink == head;
+}
+
+static __inline void sym_que_splice(struct sym_quehead *list,
+       struct sym_quehead *head)
+{
+       struct sym_quehead *first = list->flink;
+
+       if (first != list) {
+               struct sym_quehead *last = list->blink;
+               struct sym_quehead *at   = head->flink;
+
+               first->blink = head;
+               head->flink  = first;
+
+               last->flink = at;
+               at->blink   = last;
+       }
+}
+
+static __inline void sym_que_move(struct sym_quehead *orig,
+       struct sym_quehead *dest)
+{
+       struct sym_quehead *first, *last;
+
+       first = orig->flink;
+       if (first != orig) {
+               first->blink = dest;
+               dest->flink  = first;
+               last = orig->blink;
+               last->flink  = dest;
+               dest->blink  = last;
+               orig->flink  = orig;
+               orig->blink  = orig;
+       } else {
+               dest->flink  = dest;
+               dest->blink  = dest;
+       }
+}
+
+#define sym_que_entry(ptr, type, member) \
+       ((type *)((char *)(ptr)-(unsigned int)(&((type *)0)->member)))
+
+
+#define sym_insque(new, pos)           __sym_que_add(new, pos, (pos)->flink)
+
+#define sym_remque(el)                 __sym_que_del((el)->blink, (el)->flink)
+
+#define sym_insque_head(new, head)     __sym_que_add(new, head, (head)->flink)
+
+static __inline struct sym_quehead *sym_remque_head(struct sym_quehead *head)
+{
+       struct sym_quehead *elem = head->flink;
+
+       if (elem != head)
+               __sym_que_del(head, elem->flink);
+       else
+               elem = 0;
+       return elem;
+}
+
+#define sym_insque_tail(new, head)     __sym_que_add(new, (head)->blink, head)
+
+static __inline struct sym_quehead *sym_remque_tail(struct sym_quehead *head)
+{
+       struct sym_quehead *elem = head->blink;
+
+       if (elem != head)
+               __sym_que_del(elem->blink, head);
+       else
+               elem = 0;
+       return elem;
+}
+
+/*
+ *  This one may be useful.
+ */
+#define FOR_EACH_QUEUED_ELEMENT(head, qp) \
+       for (qp = (head)->flink; qp != (head); qp = qp->flink)
+/*
+ *  FreeBSD does not offer our kind of queue in the CAM CCB.
+ *  So, we have to cast.
+ */
+#define sym_qptr(p)    ((struct sym_quehead *) (p))
+
+/*
+ *  Simple bitmap operations.
+ */ 
+#define sym_set_bit(p, n)      (((u32 *)(p))[(n)>>5] |=  (1<<((n)&0x1f)))
+#define sym_clr_bit(p, n)      (((u32 *)(p))[(n)>>5] &= ~(1<<((n)&0x1f)))
+#define sym_is_bit(p, n)       (((u32 *)(p))[(n)>>5] &   (1<<((n)&0x1f)))
+
+/*
+ *  Portable but silly implemented byte order primitives.
+ */
+#if    BYTE_ORDER == BIG_ENDIAN
+
+#define __revb16(x) (  (((u16)(x) & (u16)0x00ffU) << 8) | \
+                       (((u16)(x) & (u16)0xff00U) >> 8)        )
+#define __revb32(x) (  (((u32)(x) & 0x000000ffU) << 24) | \
+                       (((u32)(x) & 0x0000ff00U) <<  8) | \
+                       (((u32)(x) & 0x00ff0000U) >>  8) | \
+                       (((u32)(x) & 0xff000000U) >> 24)        )
+
+#define __htole16(v)   __revb16(v)
+#define __htole32(v)   __revb32(v)
+#define __le16toh(v)   __htole16(v)
+#define __le32toh(v)   __htole32(v)
+
+static __inline u16    _htole16(u16 v) { return __htole16(v); }
+static __inline u32    _htole32(u32 v) { return __htole32(v); }
+#define _le16toh       _htole16
+#define _le32toh       _htole32
+
+#else  /* LITTLE ENDIAN */
+
+#define __htole16(v)   (v)
+#define __htole32(v)   (v)
+#define __le16toh(v)   (v)
+#define __le32toh(v)   (v)
+
+#define _htole16(v)    (v)
+#define _htole32(v)    (v)
+#define _le16toh(v)    (v)
+#define _le32toh(v)    (v)
+
+#endif /* BYTE_ORDER */
+
+/*
+ * The below round up/down macros are to be used with a constant 
+ * as argument (sizeof(...) for example), for the compiler to 
+ * optimize the whole thing.
+ */
+#define _U_(a,m)       (a)<=(1<<m)?m:
+#define _D_(a,m)       (a)<(1<<(m+1))?m:
+
+/*
+ * Round up logarithm to base 2 of a 16 bit constant.
+ */
+#define _LGRU16_(a) \
+( \
+ _U_(a, 0)_U_(a, 1)_U_(a, 2)_U_(a, 3)_U_(a, 4)_U_(a, 5)_U_(a, 6)_U_(a, 7) \
+ _U_(a, 8)_U_(a, 9)_U_(a,10)_U_(a,11)_U_(a,12)_U_(a,13)_U_(a,14)_U_(a,15) \
+ 16)
+
+/*
+ * Round down logarithm to base 2 of a 16 bit constant.
+ */
+#define _LGRD16_(a) \
+( \
+ _D_(a, 0)_D_(a, 1)_D_(a, 2)_D_(a, 3)_D_(a, 4)_D_(a, 5)_D_(a, 6)_D_(a, 7) \
+ _D_(a, 8)_D_(a, 9)_D_(a,10)_D_(a,11)_D_(a,12)_D_(a,13)_D_(a,14)_D_(a,15) \
+ 16)
+
+/*
+ * Round up a 16 bit constant to the nearest power of 2.
+ */
+#define _SZRU16_(a) ((a)==0?0:(1<<_LGRU16_(a)))
+
+/*
+ * Round down a 16 bit constant to the nearest power of 2.
+ */
+#define _SZRD16_(a) ((a)==0?0:(1<<_LGRD16_(a)))
+
+#endif /* SYM_MISC_H */
diff --git a/xen/drivers/scsi/sym53c8xx_2/sym_nvram.c b/xen/drivers/scsi/sym53c8xx_2/sym_nvram.c
new file mode 100644 (file)
index 0000000..4c75662
--- /dev/null
@@ -0,0 +1,730 @@
+/*
+ * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 
+ * of PCI-SCSI IO processors.
+ *
+ * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr>
+ *
+ * This driver is derived from the Linux sym53c8xx driver.
+ * Copyright (C) 1998-2000  Gerard Roudier
+ *
+ * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 
+ * a port of the FreeBSD ncr driver to Linux-1.2.13.
+ *
+ * The original ncr driver has been written for 386bsd and FreeBSD by
+ *         Wolfgang Stanglmeier        <wolf@cologne.de>
+ *         Stefan Esser                <se@mi.Uni-Koeln.de>
+ * Copyright (C) 1994  Wolfgang Stanglmeier
+ *
+ * Other major contributions:
+ *
+ * NVRAM detection and reading.
+ * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk>
+ *
+ *-----------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef __FreeBSD__
+#include <dev/sym/sym_glue.h>
+#else
+#include "sym_glue.h"
+#endif
+
+/*
+ *  Some poor and bogus sync table that refers to Tekram NVRAM layout.
+ */
+#if SYM_CONF_NVRAM_SUPPORT
+static u_char Tekram_sync[16] =
+       {25,31,37,43, 50,62,75,125, 12,15,18,21, 6,7,9,10};
+#ifdef SYM_CONF_DEBUG_NVRAM
+static u_char Tekram_boot_delay[7] = {3, 5, 10, 20, 30, 60, 120};
+#endif
+#endif
+
+/*
+ *  Get host setup from NVRAM.
+ */
+void sym_nvram_setup_host (hcb_p np, struct sym_nvram *nvram)
+{
+#if SYM_CONF_NVRAM_SUPPORT
+       /*
+        *  Get parity checking, host ID, verbose mode 
+        *  and miscellaneous host flags from NVRAM.
+        */
+       switch(nvram->type) {
+       case SYM_SYMBIOS_NVRAM:
+               if (!(nvram->data.Symbios.flags & SYMBIOS_PARITY_ENABLE))
+                       np->rv_scntl0  &= ~0x0a;
+               np->myaddr = nvram->data.Symbios.host_id & 0x0f;
+               if (nvram->data.Symbios.flags & SYMBIOS_VERBOSE_MSGS)
+                       np->verbose += 1;
+               if (nvram->data.Symbios.flags1 & SYMBIOS_SCAN_HI_LO)
+                       np->usrflags |= SYM_SCAN_TARGETS_HILO;
+               if (nvram->data.Symbios.flags2 & SYMBIOS_AVOID_BUS_RESET)
+                       np->usrflags |= SYM_AVOID_BUS_RESET;
+               break;
+       case SYM_TEKRAM_NVRAM:
+               np->myaddr = nvram->data.Tekram.host_id & 0x0f;
+               break;
+       default:
+               break;
+       }
+#endif
+}
+
+/*
+ *  Get target setup from NVRAM.
+ */
+#if SYM_CONF_NVRAM_SUPPORT
+static void sym_Symbios_setup_target(hcb_p np,int target, Symbios_nvram *nvram);
+static void sym_Tekram_setup_target(hcb_p np,int target, Tekram_nvram *nvram);
+#endif
+
+void sym_nvram_setup_target (hcb_p np, int target, struct sym_nvram *nvp)
+{
+#if SYM_CONF_NVRAM_SUPPORT
+       switch(nvp->type) {
+       case SYM_SYMBIOS_NVRAM:
+               sym_Symbios_setup_target (np, target, &nvp->data.Symbios);
+               break;
+       case SYM_TEKRAM_NVRAM:
+               sym_Tekram_setup_target (np, target, &nvp->data.Tekram);
+               break;
+       default:
+               break;
+       }
+#endif
+}
+
+#if SYM_CONF_NVRAM_SUPPORT
+/*
+ *  Get target set-up from Symbios format NVRAM.
+ */
+static void
+sym_Symbios_setup_target(hcb_p np, int target, Symbios_nvram *nvram)
+{
+       tcb_p tp = &np->target[target];
+       Symbios_target *tn = &nvram->target[target];
+
+       tp->tinfo.user.period = tn->sync_period ? (tn->sync_period + 3) / 4 : 0;
+       tp->tinfo.user.width  = tn->bus_width == 0x10 ? BUS_16_BIT : BUS_8_BIT;
+       tp->usrtags =
+               (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SYM_SETUP_MAX_TAG : 0;
+
+       if (!(tn->flags & SYMBIOS_DISCONNECT_ENABLE))
+               tp->usrflags &= ~SYM_DISC_ENABLED;
+       if (!(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME))
+               tp->usrflags |= SYM_SCAN_BOOT_DISABLED;
+       if (!(tn->flags & SYMBIOS_SCAN_LUNS))
+               tp->usrflags |= SYM_SCAN_LUNS_DISABLED;
+}
+
+/*
+ *  Get target set-up from Tekram format NVRAM.
+ */
+static void
+sym_Tekram_setup_target(hcb_p np, int target, Tekram_nvram *nvram)
+{
+       tcb_p tp = &np->target[target];
+       struct Tekram_target *tn = &nvram->target[target];
+       int i;
+
+       if (tn->flags & TEKRAM_SYNC_NEGO) {
+               i = tn->sync_index & 0xf;
+               tp->tinfo.user.period = Tekram_sync[i];
+       }
+
+       tp->tinfo.user.width =
+               (tn->flags & TEKRAM_WIDE_NEGO) ? BUS_16_BIT : BUS_8_BIT;
+
+       if (tn->flags & TEKRAM_TAGGED_COMMANDS) {
+               tp->usrtags = 2 << nvram->max_tags_index;
+       }
+
+       if (tn->flags & TEKRAM_DISCONNECT_ENABLE)
+               tp->usrflags |= SYM_DISC_ENABLED;
+       /* If any device does not support parity, we will not use this option */
+       if (!(tn->flags & TEKRAM_PARITY_CHECK))
+               np->rv_scntl0  &= ~0x0a; /* SCSI parity checking disabled */
+}
+
+#ifdef SYM_CONF_DEBUG_NVRAM
+/*
+ *  Dump Symbios format NVRAM for debugging purpose.
+ */
+static void sym_display_Symbios_nvram(sdev_p np, Symbios_nvram *nvram)
+{
+       int i;
+
+       /* display Symbios nvram host data */
+       printf("%s: HOST ID=%d%s%s%s%s%s%s\n",
+               sym_name(np), nvram->host_id & 0x0f,
+               (nvram->flags  & SYMBIOS_SCAM_ENABLE)   ? " SCAM"       :"",
+               (nvram->flags  & SYMBIOS_PARITY_ENABLE) ? " PARITY"     :"",
+               (nvram->flags  & SYMBIOS_VERBOSE_MSGS)  ? " VERBOSE"    :"", 
+               (nvram->flags  & SYMBIOS_CHS_MAPPING)   ? " CHS_ALT"    :"", 
+               (nvram->flags2 & SYMBIOS_AVOID_BUS_RESET)?" NO_RESET"   :"",
+               (nvram->flags1 & SYMBIOS_SCAN_HI_LO)    ? " HI_LO"      :"");
+
+       /* display Symbios nvram drive data */
+       for (i = 0 ; i < 15 ; i++) {
+               struct Symbios_target *tn = &nvram->target[i];
+               printf("%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
+               sym_name(np), i,
+               (tn->flags & SYMBIOS_DISCONNECT_ENABLE) ? " DISC"       : "",
+               (tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME) ? " SCAN_BOOT"  : "",
+               (tn->flags & SYMBIOS_SCAN_LUNS)         ? " SCAN_LUNS"  : "",
+               (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ"        : "",
+               tn->bus_width,
+               tn->sync_period / 4,
+               tn->timeout);
+       }
+}
+
+/*
+ *  Dump TEKRAM format NVRAM for debugging purpose.
+ */
+static void sym_display_Tekram_nvram(sdev_p np, Tekram_nvram *nvram)
+{
+       int i, tags, boot_delay;
+       char *rem;
+
+       /* display Tekram nvram host data */
+       tags = 2 << nvram->max_tags_index;
+       boot_delay = 0;
+       if (nvram->boot_delay_index < 6)
+               boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
+       switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
+       default:
+       case 0: rem = "";                       break;
+       case 1: rem = " REMOVABLE=boot device"; break;
+       case 2: rem = " REMOVABLE=all";         break;
+       }
+
+       printf("%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
+               sym_name(np), nvram->host_id & 0x0f,
+               (nvram->flags1 & SYMBIOS_SCAM_ENABLE)   ? " SCAM"       :"",
+               (nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES":"",
+               (nvram->flags & TEKRAM_DRIVES_SUP_1GB)  ? " >1GB"       :"",
+               (nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET"    :"",
+               (nvram->flags & TEKRAM_ACTIVE_NEGATION) ? " ACT_NEG"    :"",
+               (nvram->flags & TEKRAM_IMMEDIATE_SEEK)  ? " IMM_SEEK"   :"",
+               (nvram->flags & TEKRAM_SCAN_LUNS)       ? " SCAN_LUNS"  :"",
+               (nvram->flags1 & TEKRAM_F2_F6_ENABLED)  ? " F2_F6"      :"",
+               rem, boot_delay, tags);
+
+       /* display Tekram nvram drive data */
+       for (i = 0; i <= 15; i++) {
+               int sync, j;
+               struct Tekram_target *tn = &nvram->target[i];
+               j = tn->sync_index & 0xf;
+               sync = Tekram_sync[j];
+               printf("%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
+               sym_name(np), i,
+               (tn->flags & TEKRAM_PARITY_CHECK)       ? " PARITY"     : "",
+               (tn->flags & TEKRAM_SYNC_NEGO)          ? " SYNC"       : "",
+               (tn->flags & TEKRAM_DISCONNECT_ENABLE)  ? " DISC"       : "",
+               (tn->flags & TEKRAM_START_CMD)          ? " START"      : "",
+               (tn->flags & TEKRAM_TAGGED_COMMANDS)    ? " TCQ"        : "",
+               (tn->flags & TEKRAM_WIDE_NEGO)          ? " WIDE"       : "",
+               sync);
+       }
+}
+#endif /* SYM_CONF_DEBUG_NVRAM */
+#endif /* SYM_CONF_NVRAM_SUPPORT */
+
+
+/*
+ *  Try reading Symbios or Tekram NVRAM
+ */
+#if SYM_CONF_NVRAM_SUPPORT
+static int sym_read_Symbios_nvram (sdev_p np, Symbios_nvram *nvram);
+static int sym_read_Tekram_nvram  (sdev_p np, Tekram_nvram *nvram);
+#endif
+
+int sym_read_nvram (sdev_p np, struct sym_nvram *nvp)
+{
+#if SYM_CONF_NVRAM_SUPPORT
+       /*
+        *  Try to read SYMBIOS nvram.
+        *  Try to read TEKRAM nvram if Symbios nvram not found.
+        */
+       if      (SYM_SETUP_SYMBIOS_NVRAM &&
+                !sym_read_Symbios_nvram (np, &nvp->data.Symbios)) {
+               nvp->type = SYM_SYMBIOS_NVRAM;
+#ifdef SYM_CONF_DEBUG_NVRAM
+               sym_display_Symbios_nvram(np, &nvp->data.Symbios);
+#endif
+       }
+       else if (SYM_SETUP_TEKRAM_NVRAM &&
+                !sym_read_Tekram_nvram (np, &nvp->data.Tekram)) {
+               nvp->type = SYM_TEKRAM_NVRAM;
+#ifdef SYM_CONF_DEBUG_NVRAM
+               sym_display_Tekram_nvram(np, &nvp->data.Tekram);
+#endif
+       }
+       else
+               nvp->type = 0;
+#else
+       nvp->type = 0;
+#endif
+       return nvp->type;
+}
+
+
+#if SYM_CONF_NVRAM_SUPPORT
+/*
+ *  24C16 EEPROM reading.
+ *
+ *  GPOI0 - data in/data out
+ *  GPIO1 - clock
+ *  Symbios NVRAM wiring now also used by Tekram.
+ */
+
+#define SET_BIT 0
+#define CLR_BIT 1
+#define SET_CLK 2
+#define CLR_CLK 3
+
+/*
+ *  Set/clear data/clock bit in GPIO0
+ */
+static void S24C16_set_bit(sdev_p np, u_char write_bit, u_char *gpreg, 
+                         int bit_mode)
+{
+       UDELAY (5);
+       switch (bit_mode){
+       case SET_BIT:
+               *gpreg |= write_bit;
+               break;
+       case CLR_BIT:
+               *gpreg &= 0xfe;
+               break;
+       case SET_CLK:
+               *gpreg |= 0x02;
+               break;
+       case CLR_CLK:
+               *gpreg &= 0xfd;
+               break;
+
+       }
+       OUTB (nc_gpreg, *gpreg);
+       UDELAY (5);
+}
+
+/*
+ *  Send START condition to NVRAM to wake it up.
+ */
+static void S24C16_start(sdev_p np, u_char *gpreg)
+{
+       S24C16_set_bit(np, 1, gpreg, SET_BIT);
+       S24C16_set_bit(np, 0, gpreg, SET_CLK);
+       S24C16_set_bit(np, 0, gpreg, CLR_BIT);
+       S24C16_set_bit(np, 0, gpreg, CLR_CLK);
+}
+
+/*
+ *  Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
+ */
+static void S24C16_stop(sdev_p np, u_char *gpreg)
+{
+       S24C16_set_bit(np, 0, gpreg, SET_CLK);
+       S24C16_set_bit(np, 1, gpreg, SET_BIT);
+}
+
+/*
+ *  Read or write a bit to the NVRAM,
+ *  read if GPIO0 input else write if GPIO0 output
+ */
+static void S24C16_do_bit(sdev_p np, u_char *read_bit, u_char write_bit, 
+                        u_char *gpreg)
+{
+       S24C16_set_bit(np, write_bit, gpreg, SET_BIT);
+       S24C16_set_bit(np, 0, gpreg, SET_CLK);
+       if (read_bit)
+               *read_bit = INB (nc_gpreg);
+       S24C16_set_bit(np, 0, gpreg, CLR_CLK);
+       S24C16_set_bit(np, 0, gpreg, CLR_BIT);
+}
+
+/*
+ *  Output an ACK to the NVRAM after reading,
+ *  change GPIO0 to output and when done back to an input
+ */
+static void S24C16_write_ack(sdev_p np, u_char write_bit, u_char *gpreg, 
+                           u_char *gpcntl)
+{
+       OUTB (nc_gpcntl, *gpcntl & 0xfe);
+       S24C16_do_bit(np, 0, write_bit, gpreg);
+       OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ *  Input an ACK from NVRAM after writing,
+ *  change GPIO0 to input and when done back to an output
+ */
+static void S24C16_read_ack(sdev_p np, u_char *read_bit, u_char *gpreg, 
+                          u_char *gpcntl)
+{
+       OUTB (nc_gpcntl, *gpcntl | 0x01);
+       S24C16_do_bit(np, read_bit, 1, gpreg);
+       OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ *  WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
+ *  GPIO0 must already be set as an output
+ */
+static void S24C16_write_byte(sdev_p np, u_char *ack_data, u_char write_data, 
+                            u_char *gpreg, u_char *gpcntl)
+{
+       int x;
+       
+       for (x = 0; x < 8; x++)
+               S24C16_do_bit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
+               
+       S24C16_read_ack(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ *  READ a byte from the NVRAM and then send an ACK to say we have got it,
+ *  GPIO0 must already be set as an input
+ */
+static void S24C16_read_byte(sdev_p np, u_char *read_data, u_char ack_data, 
+                           u_char *gpreg, u_char *gpcntl)
+{
+       int x;
+       u_char read_bit;
+
+       *read_data = 0;
+       for (x = 0; x < 8; x++) {
+               S24C16_do_bit(np, &read_bit, 1, gpreg);
+               *read_data |= ((read_bit & 0x01) << (7 - x));
+       }
+
+       S24C16_write_ack(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ *  Read 'len' bytes starting at 'offset'.
+ */
+static int sym_read_S24C16_nvram (sdev_p np, int offset, u_char *data, int len)
+{
+       u_char  gpcntl, gpreg;
+       u_char  old_gpcntl, old_gpreg;
+       u_char  ack_data;
+       int     retv = 1;
+       int     x;
+
+       /* save current state of GPCNTL and GPREG */
+       old_gpreg       = INB (nc_gpreg);
+       old_gpcntl      = INB (nc_gpcntl);
+       gpcntl          = old_gpcntl & 0x1c;
+
+       /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
+       OUTB (nc_gpreg,  old_gpreg);
+       OUTB (nc_gpcntl, gpcntl);
+
+       /* this is to set NVRAM into a known state with GPIO0/1 both low */
+       gpreg = old_gpreg;
+       S24C16_set_bit(np, 0, &gpreg, CLR_CLK);
+       S24C16_set_bit(np, 0, &gpreg, CLR_BIT);
+               
+       /* now set NVRAM inactive with GPIO0/1 both high */
+       S24C16_stop(np, &gpreg);
+       
+       /* activate NVRAM */
+       S24C16_start(np, &gpreg);
+
+       /* write device code and random address MSB */
+       S24C16_write_byte(np, &ack_data,
+               0xa0 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
+       if (ack_data & 0x01)
+               goto out;
+
+       /* write random address LSB */
+       S24C16_write_byte(np, &ack_data,
+               offset & 0xff, &gpreg, &gpcntl);
+       if (ack_data & 0x01)
+               goto out;
+
+       /* regenerate START state to set up for reading */
+       S24C16_start(np, &gpreg);
+       
+       /* rewrite device code and address MSB with read bit set (lsb = 0x01) */
+       S24C16_write_byte(np, &ack_data,
+               0xa1 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
+       if (ack_data & 0x01)
+               goto out;
+
+       /* now set up GPIO0 for inputting data */
+       gpcntl |= 0x01;
+       OUTB (nc_gpcntl, gpcntl);
+               
+       /* input all requested data - only part of total NVRAM */
+       for (x = 0; x < len; x++) 
+               S24C16_read_byte(np, &data[x], (x == (len-1)), &gpreg, &gpcntl);
+
+       /* finally put NVRAM back in inactive mode */
+       gpcntl &= 0xfe;
+       OUTB (nc_gpcntl, gpcntl);
+       S24C16_stop(np, &gpreg);
+       retv = 0;
+out:
+       /* return GPIO0/1 to original states after having accessed NVRAM */
+       OUTB (nc_gpcntl, old_gpcntl);
+       OUTB (nc_gpreg,  old_gpreg);
+
+       return retv;
+}
+
+#undef SET_BIT
+#undef CLR_BIT
+#undef SET_CLK
+#undef CLR_CLK
+
+/*
+ *  Try reading Symbios NVRAM.
+ *  Return 0 if OK.
+ */
+static int sym_read_Symbios_nvram (sdev_p np, Symbios_nvram *nvram)
+{
+       static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
+       u_char *data = (u_char *) nvram;
+       int len  = sizeof(*nvram);
+       u_short csum;
+       int x;
+
+       /* probe the 24c16 and read the SYMBIOS 24c16 area */
+       if (sym_read_S24C16_nvram (np, SYMBIOS_NVRAM_ADDRESS, data, len))
+               return 1;
+
+       /* check valid NVRAM signature, verify byte count and checksum */
+       if (nvram->type != 0 ||
+           bcmp(nvram->trailer, Symbios_trailer, 6) ||
+           nvram->byte_count != len - 12)
+               return 1;
+
+       /* verify checksum */
+       for (x = 6, csum = 0; x < len - 6; x++)
+               csum += data[x];
+       if (csum != nvram->checksum)
+               return 1;
+
+       return 0;
+}
+
+/*
+ *  93C46 EEPROM reading.
+ *
+ *  GPOI0 - data in
+ *  GPIO1 - data out
+ *  GPIO2 - clock
+ *  GPIO4 - chip select
+ *
+ *  Used by Tekram.
+ */
+
+/*
+ *  Pulse clock bit in GPIO0
+ */
+static void T93C46_Clk(sdev_p np, u_char *gpreg)
+{
+       OUTB (nc_gpreg, *gpreg | 0x04);
+       UDELAY (2);
+       OUTB (nc_gpreg, *gpreg);
+}
+
+/* 
+ *  Read bit from NVRAM
+ */
+static void T93C46_Read_Bit(sdev_p np, u_char *read_bit, u_char *gpreg)
+{
+       UDELAY (2);
+       T93C46_Clk(np, gpreg);
+       *read_bit = INB (nc_gpreg);
+}
+
+/*
+ *  Write bit to GPIO0
+ */
+static void T93C46_Write_Bit(sdev_p np, u_char write_bit, u_char *gpreg)
+{
+       if (write_bit & 0x01)
+               *gpreg |= 0x02;
+       else
+               *gpreg &= 0xfd;
+               
+       *gpreg |= 0x10;
+               
+       OUTB (nc_gpreg, *gpreg);
+       UDELAY (2);
+
+       T93C46_Clk(np, gpreg);
+}
+
+/*
+ *  Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
+ */
+static void T93C46_Stop(sdev_p np, u_char *gpreg)
+{
+       *gpreg &= 0xef;
+       OUTB (nc_gpreg, *gpreg);
+       UDELAY (2);
+
+       T93C46_Clk(np, gpreg);
+}
+
+/*
+ *  Send read command and address to NVRAM
+ */
+static void T93C46_Send_Command(sdev_p np, u_short write_data, 
+                               u_char *read_bit, u_char *gpreg)
+{
+       int x;
+
+       /* send 9 bits, start bit (1), command (2), address (6)  */
+       for (x = 0; x < 9; x++)
+               T93C46_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
+
+       *read_bit = INB (nc_gpreg);
+}
+
+/*
+ *  READ 2 bytes from the NVRAM
+ */
+static void T93C46_Read_Word(sdev_p np, u_short *nvram_data, u_char *gpreg)
+{
+       int x;
+       u_char read_bit;
+
+       *nvram_data = 0;
+       for (x = 0; x < 16; x++) {
+               T93C46_Read_Bit(np, &read_bit, gpreg);
+
+               if (read_bit & 0x01)
+                       *nvram_data |=  (0x01 << (15 - x));
+               else
+                       *nvram_data &= ~(0x01 << (15 - x));
+       }
+}
+
+/*
+ *  Read Tekram NvRAM data.
+ */
+static int T93C46_Read_Data(sdev_p np, u_short *data,int len,u_char *gpreg)
+{
+       u_char  read_bit;
+       int     x;
+
+       for (x = 0; x < len; x++)  {
+
+               /* output read command and address */
+               T93C46_Send_Command(np, 0x180 | x, &read_bit, gpreg);
+               if (read_bit & 0x01)
+                       return 1; /* Bad */
+               T93C46_Read_Word(np, &data[x], gpreg);
+               T93C46_Stop(np, gpreg);
+       }
+
+       return 0;
+}
+
+/*
+ *  Try reading 93C46 Tekram NVRAM.
+ */
+static int sym_read_T93C46_nvram (sdev_p np, Tekram_nvram *nvram)
+{
+       u_char gpcntl, gpreg;
+       u_char old_gpcntl, old_gpreg;
+       int retv = 1;
+
+       /* save current state of GPCNTL and GPREG */
+       old_gpreg       = INB (nc_gpreg);
+       old_gpcntl      = INB (nc_gpcntl);
+
+       /* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
+          1/2/4 out */
+       gpreg = old_gpreg & 0xe9;
+       OUTB (nc_gpreg, gpreg);
+       gpcntl = (old_gpcntl & 0xe9) | 0x09;
+       OUTB (nc_gpcntl, gpcntl);
+
+       /* input all of NVRAM, 64 words */
+       retv = T93C46_Read_Data(np, (u_short *) nvram,
+                               sizeof(*nvram) / sizeof(short), &gpreg);
+       
+       /* return GPIO0/1/2/4 to original states after having accessed NVRAM */
+       OUTB (nc_gpcntl, old_gpcntl);
+       OUTB (nc_gpreg,  old_gpreg);
+
+       return retv;
+}
+
+/*
+ *  Try reading Tekram NVRAM.
+ *  Return 0 if OK.
+ */
+static int sym_read_Tekram_nvram (sdev_p np, Tekram_nvram *nvram)
+{
+       u_char *data = (u_char *) nvram;
+       int len = sizeof(*nvram);
+       u_short csum;
+       int x;
+
+       switch (np->device_id) {
+       case PCI_ID_SYM53C885:
+       case PCI_ID_SYM53C895:
+       case PCI_ID_SYM53C896:
+               x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
+                                         data, len);
+               break;
+       case PCI_ID_SYM53C875:
+               x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
+                                         data, len);
+               if (!x)
+                       break;
+       default:
+               x = sym_read_T93C46_nvram(np, nvram);
+               break;
+       }
+       if (x)
+               return 1;
+
+       /* verify checksum */
+       for (x = 0, csum = 0; x < len - 1; x += 2)
+               csum += data[x] + (data[x+1] << 8);
+       if (csum != 0x1234)
+               return 1;
+
+       return 0;
+}
+
+#endif /* SYM_CONF_NVRAM_SUPPORT */